in reply to Re^3: Trying to understand closures
in thread Trying to understand closures

When the "my $x;" statement is compiled, it creates a slot for a lexical. When the "foo" and "bar" subroutines are compiled, they "close around" the instance of $x that is in that slot and will forever use that instance. The first time through the loop, the run-time effect of the "my $x;" statement is that "this lexical hasn't been used yet so I don't need to waste time re-initializing it". The second time through the loop, the run-time effect of the "my $x;" statement needs to create a new instance of $x because that instance has been used and is still being reference and so can't just be re-used. At this point, the $x being referenced in the loop is different from the $x being referenced in the closures (subs).

I'm not surprised that the first time a "my" statement gets run, that it knows to not waste time creating a new instance. I don't know the technical details of how it determines that it is the first time that it has been run, or, if that isn't what it is determining, why it doesn't consider the $x instance that was already manipulated by the closures worthy of being replaced by a new instance. Or perhaps the "create a new instance" actually happens when the scope is left rather than when it is re-entered. I'd be interested in more details on this from someone more familiar with Perl guts.

Consider:

for( 1..3 ) { foo(); REDO: foo(); my $x; sub foo { print 'f', ++$x, $/; } print ++$x, $/; goto REDO if 2 == $_++; } __END__ f1 f2 3 f4 f5 1 f6 2 f7 f8 1

Note that the "2" line shows that the goto re-running the "my" statement doesn't re-initialize the $x lexical. This might support the theory that a new $x instance is created when the lexical scope is left. I might call this a "bug" except for this is too esoteric and I don't think code should depend on such so I don't care. But it is interesting.

- tye        

Replies are listed 'Best First'.
Re^5: Trying to understand closures (instances)
by ikegami (Patriarch) on Nov 23, 2006 at 09:15 UTC

    Or perhaps the "create a new instance" actually happens when the scope is left rather than when it is re-entered.

    That's what a monk told me. He even told me the name of the macro, but it escapes me. Unfortunately, I lost the notes I took.

    ( Update: I did some digging in the Perl sources. Opcode pp_padsv (called to fetch an SV from the pad) adds a SAVEt_CLEARSV "directive" on the stack (if I understand correctly). When the scope is exited, leave_scope is called. When leave_scope encounters the SAVEt_CLEARSV "directive", it either reinitializes the SV (using SvOK_off) or it creates a new SV to replace the still-referenced one on the pad. Similar events occur for AVs and HVs. )

    The following snippet demonstrates it's the refcount at end of the loop that matters, thus demonstrating the initialization happens at the end of the loop:

    my @a; for my $i (1..8) { foreach ('once') { my $x; push @a, \$x if $i >= 4; print(\$x, "\n"); } pop @a if $i >= 7; }

    Output:

    SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x2252e0) SCALAR(0x2252f8) SCALAR(0x225334) <-- different SCALAR(0x22513c) <-- different

    If it was the ref count on entering the loop that mattered, the output would be:

    SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x226024) SCALAR(0x2252e0) SCALAR(0x2252f8) SCALAR(0x2252f8) <-- same as prev SCALAR(0x2252f8) <-- same as prev

    Unfortunately, PadWalker can't examine the loop's pad from outside of the loop (although it can examine a sub's pad from outside the sub).