in reply to Re: How do closures and variable scope (my,our,local) interact in perl?
in thread How do closures and variable scope (my,our,local) interact in perl?

Wow. @colors was modified. Didn't think of that. That explains a lot. So it all boils down to a subtle difference between localizing and aliasing:

In the above quote from perlsyn it says both my and our are localized and doesn't make a distinction between aliasing and localizing. In your opinion is this a documentation bug? a perl bug? or neither?

Thanks, beth

Update: LanX's comment below is helpful here. He points out that lexical variables (i.e. my $name) can't be localized, so temporary aliasing is a way of faking it. Inside the loop itself, temporary aliasing is pretty much indistinguishable from localizing, but the differences between the two (localization and aliasing) become much more noticable if the variable is captured by a closure.

Replies are listed 'Best First'.
Re^3: How do closures and variable scope (my,our,local) interact in perl?
by ikegami (Patriarch) on Jun 16, 2009 at 17:57 UTC

    t gets the localized value whenever $main::name is localized and the global value when not

    No. When $name is executed, it gets the current value of $main::name. There's nothing conditional about it.

    Any assignment to $main::name within the closure changes the global variable at whatever localization level it happens to be running in.

    Again, it simply changes the variable $main::name. There's nothing conditional about it. Localisation just means a backed-up value will be assigned to the variable later.

    Inside the foreach loop, the subroutine closes over whatever $name happens to be aliased to,

    It captures the variable, whether it's an alias or not.

    No matter where the subroutine runs, any assignment to $name within the closure changes the thing aliased, i.e. an element of @colors

    If the captured variable is an alias, yes. That's what an alias is. It's got nothing to do with captures.

    In the above quote from perlsyn it says both my and our are localized and doesn't make a distinction between aliasing and localizing. In your opinion is this a documentation bug? a perl bug? or neither?

    They're independent.

    • "for my $x" creates(*) $x and aliases it.
    • "my $x; for $x" localises $x and aliases it.
    • "for our $x" localises $x and aliases it.

    I don't see anything wrong in perlsyn. Which statement is giving you pause?

    * — In practice, my vars aren't actually created at declaration and destroyed at scope exit, but that's how they're specified to behave. In reality, it might actually simply be a localisation in this case.

      Perhaps we are just going in word circles here, but it seems to me that an enclosed global does in fact get whatever happens to be the localized value of its current context. In this example below, the sub is defined in the outer block, but when it runs in a block where $x is localized, it uses the localized value, not the value from the outer block.

      prints out

      ----- our $x; for $x ...; after for block --------- setting $x to 'x' doubling $x: <x|x> localized $x to 'X' doubling $x: <X|X> leaving localization block doubling $x: <x|x|x|x>

      But maybe what you are trying to say is that the way I talked about "localization" indicates a misunderstanding? I was thinking that localization "overlay" the variable with a new variable, but it seems that you are saying that it doesn't do that at all: rather it shoves the current value off into a side area, sets a new value, and the copies the old value back at the end of the block. Do I have that right?

      As for the troubling difference, I suppose I think of file scope my variables as the "same kind" of variable as "our" variables, just private to a file. My guess is that you do not - for one - there are some major differences: our variables can be localized; my variables, even file scope my variables, cannot.

      All the same, outside of the for loop I expect anything associated with file scope $x to have the value assigned to file scope $x. But that is not what happens. Consider what happens if we use my $x; for $x.... In this case, running the sub after the for block is not affected at *all* by the current value of $x, even though $x was declared as a file scope lexical. It does, however, make and pick up changes to @colors as this code and output shows:

      outputs

      ----- my $x; for $x ... --------- set $x to 'x' $colors[0]=<red> $x=<x> doubling $x: <red|red> $colors[0]=<red|red> $x=<x> setting $colors[0] to 'RED' doubling $x: <RED|RED> doubling $x: <RED|RED|RED|RED>

      In other words, when captured by a closure, the our variable loses its aliased association outside of the for loop, the file scope my variable does not [ inside the closure ].

      Best, beth

      Update: clarification in square brackets. As ikegami point out below, my $x not in the closure does in fact lose its aliasing.

        Your code shows is that it prints the current value of $main::x, as set using = or by local restoring the backed up value.

        it seems to me that an enclosed global does in fact get whatever happens to be the localized value of its current context.

        I have no idea what that means. "Enclosed global" is a contradiction. Values aren't localised, variables are. (It backups up their value and restores it later.)

        when it runs in a block where $x is localized, it uses the localized value, not the value from the outer block.

        Again, "localised value" makes no sense.

        The "when" is misleading. There's nothing conditional about it. The same would apply if it wasn't localised. The whole sentence could be replaced with "It uses the current value".

        I was thinking that localization "overlay" the variable with a new variable,

        Localisation

        1. Backups the variable (to be restored on scope exit).
        2. Creates a new SV.
        3. Aliases the variable to that SV.

        But it's still the same variable. Any change to the variable will be seen globally.

        $y = 123; sub f { print("$y\n"); } # 456 { local $y = 456; f(); } # Same var

        That differs from my which actually creates a new var.

        my $x = 123; sub f { print("$x\n"); } # 123 { my $x = 456; f(); } # Different var
        [ The parent post initially ended at "Do I have that right?". This addresses the second half ]

        As for the troubling difference, I suppose I think of file scope my variables as the "same kind" of variable as "our" variables, just private to a file.

        There's nothing special about the file scope. It's just like every other scope. All the differences between lex and pkg vars (access, ability to capture, lifespan, etc) exist whether they are in a block or nested deeper.

        All the same, outside of the for loop I expect anything associated with file scope $x to have the value assigned to file scope $x. But that is not what happens.

        I explained what happens here.

        In short, when you capture, you link to the SV associated with the captured name. Since foreach loops change which SV is associated with the iterator each pass of the loop ("aliasing"), you capture something different every pass of the loop.

        when captured by a closure, the our variable loses its aliased association outside of the for loop, the file scope my variable does not.

        The last bit is wrong. It does indeed lose its aliasing (and localisation).

        my $y = 5; my $x; for $x ($y) { ++$x; print "$y\n"; # 6 } ++$x; print "$y\n"; # 6

        For package variables, the name is captured. The SV is looked up in the symbol table. If a different SV is later associated with the captured variable, that SV will be used.

        For lexical variables, the SV is captured. If a different SV is later associated with the captured variable, the will not be closure won't know about it.

Re^3: How do closures and variable scope (my,our,local) interact in perl?
by shmem (Chancellor) on Jun 16, 2009 at 17:56 UTC

    I'd say neither, since

    qwurx [shmem] ~ > perl -le 'local my $foo' Can't localize lexical variable $foo at -e line 1.

    localizing in that context doesn't mean a local opcode is involved; rather, a local instance of whatever thing the loop variable is will be allocated. So localizing means that, for the loop variable, but aliasing is what happens to the current element of the list iterated over. Your code is a fine example for my/local, space/time (was: Re: The difference between my and local). The aliasing happens no matter what scoping rules apply to the loop variable:

    our $name; for $name (qw(red blue green yellow orange purple violet)) { $name = "foo"; } __END__ Modification of a read-only value attempted at - line 3.

    But since an our localized variable works in time, at calling time $name is just the localized instance of $main::name, and the list for which it was used to iterate over has gone.