codesections
codesections

Reputation: 9600

Dynamic variables, CALLERS, Scalars, and assignment

I recently noticed that that re-initializing dynamic variables does not have the semantics I expected in most cases using assignment (binding works the way I expected it to, however).

Specifically, in this code:

sub g {
    my $*i  = CALLERS::<$*i>  // 0;
    my $*a1 = CALLERS::<$*a1> // Array.new;
    my @*a2 = CALLERS::<@*a2> // Array.new;
    $*i++;
    $*a1.push: 'v1';
    @*a2.push: 'v2';
    dd $*i;
    dd $*a1;
    dd @*a2;
}
sub f {
    my $*i = 0;
    my $*a1 = Array.new;
    my @*a2 = Array.new;
    g; g; g;
}
f

I expected output of 3, ["v1", "v1", "v1"], and ["v2", "v2", "v2"] but instead get 1, $["v1", "v1", "v1"], ["v2"]. Switching to binding solves the issue, so there's no problem I'm trying to solve – but I would very much like to understand why assignment doesn't work here. I notice that a Scalar pointing to an Array works, but a Scalar pointing to an Int doesn't. But in either case, I would have thought that the newly assigned variable would receive the value from CALLERS. What am I missing about the semantics of assignment?

Upvotes: 8

Views: 142

Answers (1)

Elizabeth Mattijsen
Elizabeth Mattijsen

Reputation: 26924

What am I missing about the semantics of assignment?

I think what you're missing, doesn't have anything to do with dynamic variables per se. I think what you're missing is the fact that:

my @a = Array.new;

is basically a noop. Because of the single argument rule, it is the same as:

my @a = ();

which is the same as:

my @a;

So, in your example in sub f, the:

my @*a2 = Array.new;

in is just setting up an empty array in a dynamic variable.

Then in sub g the:

my @*a2 = CALLERS::<@*a2> // Array.new;

is just basically doing (because of the single argument rule):

my @*a2;

and hence you don't see the pushes you've done before, because every call it starts out fresh.

Regarding the value of $*i in sub g: that also is just incrementing a copy of the callers $*i, so the value remains at 1 in every call.

The reason that $*a1 works, is that containerization stops the flattening behaviour of the single argument rule. Observe the difference between:

sub a(+@a) { dd @a }; a [2,3,4];  # [2,3,4]

and:

sub a(+@a) { dd @a }; a $[2,3,4];  # [[2,3,4],]

Upvotes: 7

Related Questions