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

I believe the key here is to understand that when a closure is created, it makes its own copy of the lexical environment; that's what distinguishes a "closure" from an ordinary procedure (sub). So whatever lexical variables are in scope now have an independent existence in the closure.
  • Comment on Re: How do closures and variable scope (my,our,local) interact in perl?

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

    That's not entirely true:

    my $name; $name = 'red'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; $name = 'blue'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; $name = '<none>'; print red(), "\n"; print blue(), "\n"; __END__ <FONT COLOR='<none>'></FONT> <FONT COLOR='<none>'></FONT>

    ... but when the same is done in a loop, with a lexical variable declared beforehand, the result changes:

    my $name; for $name ('red', 'blue') { *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; }; $name = '<none>'; print red(), "\n"; print blue(), "\n"; __END__ <FONT COLOR='red'></FONT> <FONT COLOR='blue'></FONT>

    The behaviour changes again, if you use a global:

    # global $name; for $name ('red', 'blue') { *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; }; $name = '<none>'; print red(), "\n"; print blue(), "\n"; __END__ <FONT COLOR='<none>'></FONT> <FONT COLOR='<none>'></FONT>
      .. but when the same is done in a loop, with a lexical variable declared beforehand, the result changes:

      because a loop is a block of it's own which changes. IIRC the essence of closures is that whenever the outer block is entered (decided at run-time!) the my variables are associated to another lexpad (please correct my terminology if I name something wrong).

      so with

      for my $var ( 1,2,3) { my $x =sub {print $var } }

      the opcode for $var points for each run into different instances of the lexpads of the for loop.

      UPDATE ----

      it's more like this in pseudocode

      while ( $LEXPAD{for-block}={}; "$LEXPAD{for-block}"->{var} = (1,2,3)- +>next() ) { my $x =sub { print "$LEXPAD{for-block}"->{var} } }

      ----- UPDATE

      OTOH there is nothing like a "closure with packagevars", they always point to the same symboltable (decided at compile-time!)

      Now the extra complexity comes because ELISHEVA declares the loop variable in advance with my / our, which implies localising, i.e. saving and restoring the variable at runtime. (Nota bene: Normally there is nothing like local() with lexvars)

      Cheers Rolf

        You'll note that I didn't use the for my version. I feel that for $name should behave identically whether $name is a lexical or a global variable, as the same variable is visible outside the for block in both cases. The two variables live in two separate namespaces, which is the likely cause for the phenomenon we see, but as their visibility is identical, that shouldn't affect behaviour. The "localising" still shouldn't affect lexical variables in a way different to global variables, but it seems to do. This might warrant some mention in the documentation.

Re^2: How do closures and variable scope (my,our,local) interact in perl?
by LanX (Saint) on Jun 16, 2009 at 15:00 UTC
    Could you please give an example? IMHO closures mean exactly the contrary of what I understand you saying.

    The problem here is that ELISHEVA is adding to the very different concepts of package vs lexical variables the dark forces of for-loops creating local aliases interfering differently with compile-time and run-time behavior of local variables.

    Cheers Rolf

    UPDATE: OK, I think the source of misunderstandings is what people mean when they say "closure". Do you mean the outer or the inner sub/block ?

      Perhaps I'm being too simple-minded, but please consider this variant:
      use strict qw(vars subs); my $name; { my $name = 'red'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; } { my $name = 'blue'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; } $name = '<none>'; print red(), "\n"; print blue(), "\n"; __END__ <FONT COLOR='red'></FONT> <FONT COLOR='blue'></FONT>
      Without the additional block delimiters ({}) and my declarations, the variable $name indeed always refers to the same storage (which, I admit, was not clear in my own mind when I wrote the above comment). Closures (red() and blue() here) only get a copy of a portion of their lexical environment (that is, become "closures", properly so-called) when they escape a region where that portion is (lexically, of course) in scope. Without a block that the thread of execution leaves, no closure is created and the code is indistinguishable from a regular sub. Note that red() and blue() (i.e. &red and &blue) exist outside the blocks because they are created in the package's (here, main) symbol table -- as using a type glob (*) always implies. Here's the code with another wrinkle to illustrate:
      use strict qw(vars subs); { my $name; { my $name = 'red'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; } { my $name = 'blue'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; } *foo = sub { print red($name), "\n"; print blue($name), "\n"; }; $name = 'another wrinkle'; } foo(); print red('hi mom'); __END__ <FONT COLOR='red'>another wrinkle</FONT> <FONT COLOR='blue'>another wrinkle</FONT> <FONT COLOR='red'>hi mom</FONT>