in reply to Trying to understand closures

If I told you, I would be the one earning the grade on your quiz, not you. ;)

Oh what the heck, who can resist?

$x exists within a lexical scope accessible by the subroutines foo() and bar(). When foo() is called, it pre-increments $x (so it becomes 1) and prints that.

Next, bar() is called on the same $x, pre-incrementing it and printing 2.

Next, the loop begins. Ignore the subroutine declarations for now, they've already taken place. 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.

Now, the loop iterates for the second time. This time we get a new lexical $x; one that hasn't been pre-incremented. And thus, 1 gets printed.

Then we iterate one more time, and get a new lexical $x again, and thus 1 gets printed.

What's cool is that the first lexical $x, the one that foo() and bar() incremented, and that got incremented on the first iteration of the loop still exists. If you call foo() after the loop, you'll get 4, indicating that foo() is still aware of and acting on the first lexical $x

As the title of your post indicates, you're playing with closures here. I know closures are discussed in the Camel book. Probably the best discussion of closures in the POD is contained in perlref, I think I recall that the first time through it I found the Camel book's discussion clearer.


Dave

Replies are listed 'Best First'.
Re^2: Trying to understand closures
by crashtest (Curate) on Nov 23, 2006 at 06:52 UTC
    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.

      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