TibetPerlMonk has asked for the wisdom of the Perl Monks concerning the following question:

If I run:

use Tk; use strict; use warnings; my $mw = MainWindow->new(); for my $index (0 .. 10) { $mw->Button(-text=>"Button $index", -command => sub {print $index} +)->pack(); } MainLoop;

The variable $index is resolved at compilation time, so when I click a button it prints the number showing on the button. However if you replace $index with $_, like this:

use Tk; use strict; use warnings; my $mw = MainWindow->new(); for (0 .. 10) { $mw->Button(-text=>"Button $_", -command => sub {print $_})->pack( +); } MainLoop;

The first $_ resolves at compile time, and the the button displays the expected title. However the second $_ follows a different set of rule, and prints a warning (unintialized blah).

Replies are listed 'Best First'.
Re: Are we seeing syntax inconsistency?
by Ovid (Cardinal) on Nov 11, 2005 at 05:05 UTC

    The warning is coming from the sub { print $_ }. Because that's an anonymous subroutine, its evaluation will be deferred until later. You don't know if something else hasn't trounced on the value of $_ by the time it's evaluated.

    The following code will issue the same warning.

    #!/usr/bin/perl use strict; use warnings; sub foo { my $sub = shift; local $_; $sub->(); } $_ = 10; foo (sub { print $_ });

    The values of the variables in the anonymous subroutine are not evaluated until the subroutine is executed. Otherwise, our lexical closures wouldn't work. You are very unlikely to see that with properly scoped lexical variables, but here's how you can make it happen:

    #!/usr/bin/perl use strict; use warnings; my $var = 10; foo(sub { print $var }); sub foo { my $sub = shift; undef $var; $sub->(); # unitialized warning }

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Are we seeing syntax inconsistency?
by blokhead (Monsignor) on Nov 11, 2005 at 05:09 UTC
    Because $index is lexically scoped, it follows different rules than $_, which is dynamically scoped.

    Each time you have a "my" variable declaration (a for-loop with a "my" variable essentially does this internally at the beginning of each iteration), it allocates a fresh memory location and binds it to the variable's name. Each pass through your loop, you create an anonymous sub that refers to the memory location of $index. But in each iteration, the name "$index" refers to a different memory location, so of your 11 different anonymous subs is pointing to a different memory location -- they don't interfere with each other... The name $index is bound to different memory locations depending on your lexical scope, so once you leave the scope where the anonymous sub was created, you can't change the value that the anonymous sub will see by changing what's in $index (well, without magic), because $index won't be pointing to the same memory location.

    On the other hand, dynamic scoping works more like global variables. $_ is always bound to the same memory location, it doesn't matter what scope you are in. So when you create 11 different anonymous subs that use $_, they are all pointing to the same memory location. If some code in some other place changes $_ (a likely scenario), then these anonymous subs will use that new value as well.

    blokhead

      So when you create 11 different anonymous subs that use $_, they are all pointing to the same memory location. If some code in some other place changes $_ (a likely scenario), then these anonymous subs will use that new value as well.
      This is true, but there is a twist. For anonymous subroutines, Perl is smart enough to notice whether they are closures or not and if not it will actually reuse the same sub reference and its associated structures each time. See the following:
      my (%withlex, %withglobal); for (1 .. 10) { my $ref = sub { return $_ }; $withglobal{$ref} = $ref; } for my $i (1 .. 10) { my $ref = sub { return $i }; $withlex{$ref} = $ref; } print "Using lex num of subs is " . (keys %withlex) . "\n"; print "Using global num of subs is " . (keys %withglobal) . "\n";
      which prints
      Using lex num of subs is 10 Using global num of subs is 1
      I also learned recently that a closure only captures the lexical variables the inner subroutine actually uses, so you can have issues like Re: Eval doesn't see lexicals. So in the OP's case, there really is only one anonymous subroutine in the $_ version.
        And just to fool around with the above code, the following example emphasizes the previous point that what matters is the current value of the global $_ when the anonymous sub runs, not its value at its time of creation. Whereas with the lexical version, what matters is the value of lexical $i at time of creation

        my (%withlex, %withglobal); for (1 .. 10) { my $ref = sub { return $_ }; $withglobal{$ref} = $ref; print $ref->(), "\n"; } print keys %withglobal; print "\n"; $_ = 'foo'; foreach my $key ( keys %withglobal ) { print "k: $key\tv: ", &{ $withglobal{$key} }(), "\n"; } for my $i (1 .. 10) { my $ref = sub { return $i }; $withlex{$ref} = $ref; print $ref->(), "\n"; } print sort { $withlex{$a} <=> $withlex{$b} } keys %withlex; print "\n"; my $i = 'bar'; foreach my $key ( sort { $withlex{$a} <=> $withlex{$b} } keys %withlex + ) { print "k: $key\tv: ", &{ $withlex{$key} }(), "\n"; } print "Using lex num of subs is " . (keys %withlex) . "\n"; print "Using global num of subs is " . (keys %withglobal) . "\n";
Re: Are we seeing syntax inconsistency?
by Aristotle (Chancellor) on Nov 11, 2005 at 05:06 UTC

    Yes. $_ is a package variable. $index is a lexical. In the first case, you have a closure which closes over $index; in the second, you simply have an anonymous function that prints a global variable.

    I do find it curious that you get 11 different $index variables, though; I would expect you’d get only one. I’m not entirely sure of how foreach works in this respect.

    Makeshifts last the longest.

      foreach has to create a fresh variable every time. Otherwise, when you keep a reference to one (as with the anonymous subroutine), they would all hold the same value every time a new value was assigned in the loop.

      (And thanks to Animator for clearing up my sloppy wording on this)

      Cheers,
      Ovid

      New address of my CGI Course.

        Actually that's not entirely correct.

        foreach does not create a variable. It is my that creates the variable. And my will use some free memory. My guess is that the references counter is increased by using the variable in an anonymous, subroutine (/closure). Which means that at the end of the foreach-block the reference counter is not 0 (and therfor the memory is not freed). Then that same part of the memory is not free at the start of the next iteration, so my will use some other memory.

        Some code:
        for my $x (0 .. 3) { print \$x }; ==> this code should print the same references for all 4 iterations.
        for my $x (0 .. 3) { print \$x; push @z, sub { $x; }; } ==>This code will print 4 different references.

Re: Are we seeing syntax inconsistency?
by blazar (Canon) on Nov 11, 2005 at 10:11 UTC

    Indeed in the first case you create a closure around a lexical variable. But $_ is not lexically scoped. So the anonymous sub you're putting on the right side of => prints $_ as it founds it when it is run. That is undef, hence the warning.

    It's just the same with:

    my @closures = map sub { $_ }, 1..5; # won't "work" my @closures = map { my $v=$_; sub { $v } } 1..5; # "works"
    In this sense it seems that starting with bleædperl 5.9.1 things will be different.
Re: Are we seeing syntax inconsistency?
by sgifford (Prior) on Nov 11, 2005 at 17:14 UTC
    In your second example, the inconsistency between the button text and the sub is because of a difference in when the code is executed. When you call $mw->Button, the code you have in your constructor call is run right then, so "Button $_" is converted to a static string, and Button saves a copy of that static string somewhere, so even if $_ changes the button's text stays the same. But the anonymous sub you give as the argument to -command isn't executed until the button is clicked. By then the value of $_ will probably have changed, and so the sub will use the current value of $_ instead of its value when the constructor was called.

    As others have said, your first example uses a closure, which implicitly creates a copy of the variable when the anonymous sub is created, so the sub refers back to that variable. Closures keep lexically-scoped variables (those created with my), but $_ isn't lexically scoped. That's the key to the difference between the two.

    You could also solve the problem by forcing the anonymous sub to resolve $_ when it's created, using eval:

    -command => eval "sub {print $_}"
    (n.b. I haven't tested this and it might not be exactly right). But using closures is much easier, faster, and more elegant.

      No, that doesn’t work in the general case. It would work here, because the string which eval sees is, say, sub { print 0 } on the first iteration. But if the loop was for( "foo bar", "baz quux" ) ){ ... }, then the eval would incorrectly be called to compile sub { print foo bar }, which may fail to compile or may compile to nonsense.

      You want to stick the value in a lexical and close over the lexical. That is the correct general solution.

      Makeshifts last the longest.

        Right, I gave that as an example to clarify what's going on, not a recommendation. Though I think it can be made to work in the general case with judicious use of quotes and quotemeta. :)