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

We've now entered the lexical scope in which foo() and bar() already pre-incremented $x when we called them earlier. On the first iteration of the loop we'll pre-increment it again, and print 3. That's probably the most counterintuitive step.

It certainly is counterintuitive! Maybe I'm fuzzy brained tonight, but I am not clear why the first iteration of the loop would be different from the second and third iterations. In other words, I would expect the output to be 12111. Why does a new lexical $x get created the second and third times through the loop, but not the first? The statment my $x; is encountered all three times.

Replies are listed 'Best First'.
Re^3: Trying to understand closures
by davido (Cardinal) on Nov 23, 2006 at 07:13 UTC

    A new lexical gets created on each iteration. But the subroutines live in the first iteration, it appears. That's why if you call foo() after the loop, you still get 4. Remember that my has both a compiletime and a runtime effect. When foo() and bar() are defined (as part of the compilation phase), the lexical scope in which they are created is ascertained at that time, and $x clearly lives in the same scope. It's a little shocking that a loop's block works the same as a bare block, but once you accept that, the rest fits together.

    Another way to think of it, and probably a more accurate way of thinking of it is that the lexical scope in which foo() and bar() are associated with $x is masked by the 2nd and 3rd iteration of the loop, when a new lexical $x of the same name is generated, thus masking previous $x's.


    Dave

      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        

        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).