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

Hi monks!

I've run into an interesting problem with closures. Here's my minimal test case:

use strict; use warnings; sub run_this { shift->() } { my $x = "hello\n"; sub foo { # print $x; run_this(sub { print $x; }); } } foo();
This compiles just fine, but if you run it $x doesn't print and an "uninitialized value" warning is generated. However, if you uncomment the first "print" statement, it works! $x is printed correctly both times.

Edit: turns out to be a bug in perl, fixed in 5.9.3. Thanks monks!

My best guess is that perl thinks that $x is not used within foo(), so it doesn't bother to keep track of it -- unless you reference $x directly within foo(). Maybe it doesn't look into the inner sub or something.

In any case, it seems to me that perl should either complain about $x in the anonymous sub at compile time, or it should just work. But I may be completely off track here.

Any ideas? Thanks!
asokoloski

Replies are listed 'Best First'.
Re: Doubly-nested deeply bound variable is undefined
by Tanktalus (Canon) on Nov 14, 2006 at 18:57 UTC

    It appears that perl does not see the use of $x in foo, and thus does not close foo on $x. But once you put in the "print $x" there, it does close foo on $x, and then the anonymous sub can close on the same $x.

    One workaround would be:

    use strict; use warnings; sub run_this { shift->() } { my $x = "hello\n"; sub foo { { no warnings; $x } run_this( sub { print $x; }); } } foo();
    Note the useless use of $x in a void context, which gets masked away with the no warnings bit. It does the same as yours as far as the problem is concerned (forcing perl to close foo on $x), but with no side effects going to standard-out ;-)

      Exactly. I guess my question is, why doesn't the use of $x inside the anonymous sub tell foo() that it needs to close over $x? Is this a bug in the perl interpreter?

      And by the way, thanks for all the answers everyone. I have the feeling that there's really no satisfying way to solve this at this time :) The workarounds are helpful, though.

        Yes, it's a bug in Perl 5.8. Dave Mitchell fixed it in bleadperl, so the code works correctly in Perl 5.9.3 and later (maybe 5.9.2).

Re: Doubly-nested deeply bound variable is undefined
by ikegami (Patriarch) on Nov 14, 2006 at 20:12 UTC

    My best guess is that perl thinks that $x is not used within foo(), so it doesn't bother to keep track of it -- unless you reference $x directly within foo(). Maybe it doesn't look into the inner sub or something.

    That's exactly it. It's probably done as an optimization. If foo doesn't use $x, it's not available to child functions either.

    Yeah, it should give a strict error.

Re: Doubly-nested deeply bound variable is undefined
by GrandFather (Saint) on Nov 14, 2006 at 18:46 UTC

    My closure fu is not strong, but my guess is that the $x that is assigned the string is not the same instance of $x that is passed to the annon sub in the run_this call. If you change the my to our you will get the behaviour you expect.


    DWIM is Perl's answer to Gödel
      Sounds right. I just don't understand why a print() statement makes perl change its mind and use the correct instance of $x. Actually, I've been playing around with it and it seems like any reference to $x inside foo() makes it work correctly, even if it's just $x; on a line by itself.
Re: Doubly-nested deeply bound variable is undefined
by swampyankee (Parson) on Nov 14, 2006 at 18:32 UTC

    Could it be related to this?

    Unlike dynamic variables created by the local operator, lexical variables declared with my are totally hidden from the outside world, including any called subroutines. This is true if it's the same subroutine called from itself or elsewhere--every call gets its own copy.
    (From perlsub).

    emc

    At that time [1909] the chief engineer was almost always the chief test pilot as well. That had the fortunate result of eliminating poor engineering early in aviation.

    —Igor Sikorsky, reported in AOPA Pilot magazine February 2003.
      I don't think that's it. I'm pretty sure that's talking about variables declared within the subroutine.

      Right after the part you mentioned, perlsub says this:

      This doesn't mean that a my variable declared in a statically enclosing lexical scope would be invisible. Only dynamic scopes are cut off. For example, the bumpx() function below has access to the lexical $x variable because both the my and the sub occurred at the same scope, presumably file scope.
      my $x = 10; sub bumpx { $x++ }
      I thought that since the lexical scope of $x includes the body of bumpx(), any closures created inside bumpx should also have access to $x -- and they do, kinda. But it seems like bumpx() (or foo(), in my case) has to be "reminded" that $x is in its scope, otherwise the inner sub doesn't get access.
Re: Doubly-nested deeply bound variable is undefined
by NetWallah (Canon) on Nov 14, 2006 at 19:08 UTC
    The code also works if you remove the braces enclosing the $x and "sub foo" declarations.

    This leads me to the following hypothesis:
    The block enclosing the $x and "sub foo" declarations does not get invoked/executed, since it is just a block declaration. When you call foo(), just the sub gets called - the enclosing block has not yet been invoked.

    If you use $x within foo, I believe a JIT block invocation takes place to instantiate $x. If you don't use $x directly, an un-initialized $x gets passed to run_this().

    I'll defer to more enlightened monks to elaborate on this theory, and comment on whether this is desirable behaviour.

         "A closed mouth gathers no feet." --Unknown

Re: Doubly-nested deeply bound variable is undefined
by Anonymous Monk on Nov 15, 2006 at 17:21 UTC
    If a closure is "a special case of an anonymous subroutine holding onto data that used to belong to its scope at the time of its creation" (S.Srinivasan in Advanced Perl Programming), then your code is behaving right, although strict has failed to detect that $x within the anonymous sub isn't referring to the same $x within the grand-parent scope.

    However, having said that, though anonymous subroutines are oblivious to lexicals in a grand-parent scope, normal subroutines aren't, also a normal subroutine might not derive and use a set of parent lexicals but its "children" subroutines might do, so the characteristic is intranstive, consider the following..
    sub test_sub { print &{+shift}; } { my $x = "hello"; my $y = "world"; local $, = " : "; print "x - local lexical1 : $x ", \$x; print "y - local lexical1 : $y ", \$y; sub foo { test_sub( sub { "x - derived lexical (anon) : $x ", \$x } ); print "y - derived lexical (foo) : $y ", \$y; test_sub( sub { "y - derived lexical (anon) : $y ", \$y } ); sub bar { sub baz { print "x - derived lexical (baz) : $x ", \$x; } } } print "x - local lexical2 : $x ", \$x; print "y - local lexical2 : $y ", \$y; } foo(); baz(); __END__ __output__ x - local lexical1 : hello : SCALAR(0x814c630) y - local lexical1 : world : SCALAR(0x814c6f0) x - local lexical2 : hello : SCALAR(0x814c630) y - local lexical2 : world : SCALAR(0x814c6f0) Use of uninitialized value in concatenation (.) or string at foo line +17. x - derived lexical (anon) : SCALAR(0x814bc28) y - derived lexical (foo) : world SCALAR(0x814c6f0) y - derived lexical (anon) : world SCALAR(0x814c6f0) x - derived lexical (baz) : hello SCALAR(0x814c630)
    Notice, that within foo() \$x refers to a different location than \$x in the parent scope.

    To better redefine a closure: "A closure is an anonymous subroutine accessing the immediate local scope at the time of its creation" (atleast for <= perl 5.8.x). So, it might be better to ensure that all lexicals needed to be squirreled away by a closure are explicitly typed within the immediate local scope.