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 ;-) | [reply] [d/l] |
|
|
| [reply] |
|
|
| [reply] |
|
|
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.
| [reply] [d/l] [select] |
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
| [reply] [d/l] [select] |
|
|
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.
| [reply] [d/l] |
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.
| [reply] |
|
|
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. | [reply] [d/l] |
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
| [reply] |
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.
| [reply] [d/l] |