in reply to Unusual Closure Behaviour

The jury is still debating whether this is a feature or a bug. All I can offer is an explanation.

my has compile-time *and* run-time effects. At compile time, the compiler knows about the variable, etc. At run-time, values are set, my $x; makes that $x becomes undef. So far, so good.

However, for efficiency reasons, if Perl exits a block, it will actually *not* delete any variables lexical to the block. You cannot refer to them anymore (the compiler takes care of that), but the data structure build for it remains. Perl does this because it is likely that you reenter a block and if the structure remains, Perl can save time rebuilding it. However, with my $x if undef, no run-time effect on $x happens when reentering the block. (The first time the block is entered, the datastructure gets build when $x is used). And since the structure doesn't get rebuild, the value doesn't get reset either. So, you have created a static variable....

-- Abigail

Replies are listed 'Best First'.
Re: Re: Unusual Closure Behaviour
by sierrathedog04 (Hermit) on Jul 12, 2001 at 21:11 UTC
    It is a bug. As it is written in the tractate Camel III 4:132:
    Lexicals are hidden from any subroutine called from their scope. This is true even if the same subroutine is called from itself or elsewhere—each instance of the subroutine gets its own "scratchpad" of lexical variables.
    $x is clearly a lexical. Its scope is inside foo. Yet each instance of foo is not getting its own scratchpad of lexical variables. Hence, we have a creepy crawly thing of uncertain appeal...a bug.

    Update 2: One might argue that the anomalous behavior of $x is consistent with the above dictum, because the subroutine foo is not being called from scope of $x. However, when the Perl wise ones write that lexicals are hidden from any subroutine called from their scope, it means even if they are called from their scope.

    If a lexical is hidden from subroutines called from inside its own scope, then how much more so must it be hidden from subroutines called from outside its own scope.

    As Abigail has taught, conditionally declaring lexicals for use in subroutines is an example of "an ox that is known to gore," i.e., its behavior is going to be ambiguous. Even though it is legal to release such an ox and let it run around and possibly damage a neighbor's code, let us instead "build a fence around the Law" and only declare lexical variables in subroutines unconditionally, outside of conditional branching such as 'my $x if 0'.

    (previous updates effaced)

      Well, technically what is happening is not violating Camel III. See, it *does* get its own scratchpad, which can be quickly checked by:
      #!/opt/perl/bin/perl -w use strict; sub foo; sub foo { return unless $_ [0]; my $x if 0; print ++ $x, " "; foo $_ [0] - 1; } foo 1; print "\n"; foo 2; print "\n"; foo 3; print "\n"; foo 4; print "\n"; __END__ 1 2 1 3 2 1 4 3 2 1
      Scary, isn't? When the subroutine recurses, it notices there is still a reference to $x and hence it will create a new scratchpad. But when there is no reference, it will reuse an old scratchpad....

      I do agree that using my in this way doesn't tend to lead to well understood code. I wouldn't go as far as to say it's never useful, but such cases will be very rare and it's not a technique I would teach in my classes.

      -- Abigail

        Have you tried this?
        #!/usr/bin/perl use strict; sub foo; sub foo { return unless $_ [0]; my $x if undef; print ++ $x, " "; foo $_ [0] - 1; } foo 29; print "\n";
        It prints:

        1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1