meonkeys has asked for the wisdom of the Perl Monks concerning the following question:

I'm seeing behavior I wouldn't expect from the following code:
#!/usr/bin/perl -w use Data::Dumper qw( Dumper ); use strict; sub mycode { my $var = $_[0]; defined($var) ? print qq|mycode() called with $var\n| : print qq|mycode() called with no operands.\n|; my $foo = "blah" if $var; # does $foo become static here? print "foo is $foo\n" if $foo; $foo ||= "FOO"; } mycode("test"); # "foo is blah" mycode(); # mycode(); # "foo is FOO" mycode(); # "foo is FOO" # print Dumper(\%main::); # make sure $foo is never in symbol table

I get this output:
mycode() called with test foo is blah mycode() called with no operands. mycode() called with no operands. foo is FOO mycode() called with no operands. foo is FOO
but I would expect this output:
mycode() called with test foo is blah mycode() called with no operands. mycode() called with no operands. mycode() called with no operands.
Somehow the variable $foo gets the value 'FOO' on the second call of mycode(). This seems to persist as seen in the third and fourth calls of mycode(). Can anyone explain this?

---
"A Jedi uses the Force for knowledge and defense, never for attack."

Replies are listed 'Best First'.
Re: static-like persistence of my variable due to trailing conditional
by shotgunefx (Parson) on Jun 30, 2002 at 01:50 UTC
    It's a *cough* feature. It's been discussed here plenty. There was also a big discussion on perl.com in May of 2000 here

    -Lee

    "To be civilized is to deny one's nature."
      Excellent. Thank you, shotgunefx. The link you provided led me to find a few good threads here at perlmonks and also on P5P (however, your link seems bad... I couldn't find 'my $x if 0' on that page).

      1. Unusual Closure Behaviour

      2. It *is* a feature: mod_perl and 'my $x if 0'

      3. Google's cache of "This Week on P5P, 2000/05/21", which seems to contain the text 'my $x if 0; Trick' that shotgunefx was leading us to.

      I guess the consensus is that this is a bug; er... feature. Anyway, I'll probably never again use trailing conditionals and initializations concurrently. Again, I am refused disappointment by the amazing Perlmonks! Thank you all.

      -Adam
      ---
      "A Jedi uses the Force for knowledge and defense, never for attack."
        No problem. It's been gone over quite a few times here but the 4 character maximum on search makes the threads hard to find.

        As an aside, the link does work for me in IE5. It's the second targeted link on the page at http://www.perl.com/pub/a/2000/05/p5pdigest/THISWEEK-20000521.html

        -Lee

        "To be civilized is to deny one's nature."
        *grin* It falls in a category that might best be described "unintended feature". Feature, because it is well within documented specifications and conforms to intended behaviour. But unintended, because noone anticipated there might be an interaction between the relevant specifications.

        Makeshifts last the longest.

Re: static-like persistence of my variable due to trailing conditional
by perrin (Chancellor) on Jun 29, 2002 at 21:46 UTC
    I seem to recall this being a bug that was discussed on p5p a couple of years ago. It caused a memory leak too I think. Something to do with an internal optimization. Matts posted something about it to the mod_perl list back then because he discovered it was causing a memory leak in AxKit.
Re: static-like persistence of my variable due to trailing conditional
by BrowserUk (Patriarch) on Jun 29, 2002 at 21:39 UTC

    My guess as to what is happening here is when you call mycode() the second time with no parameters, the my $foo = "blah" is not executed because the if $var condition.

    Therefore, the $foo referenced in the $foo ||= "FOO"; line is a MAIN::$foo not a local one. Hence the output from the third and fourth calls.

    Update: In the light of hossman's post I guess I guessed wrong!

    I must stop guessing; I must stop guessing

      The problem is definitely that the whole line "my $foo = "blah" if $var;" isn't getting executed (including the my $foo declaration) ... you can see the expected behavior if you change it to : "my $foo; $foo = "blah" if $var;"

      But a global $foo isn't getting set -- otherwise "-w/use strict" would complain.

      I honestly have no idea what in the semantic rules of Perl describes the behavior you are seeing. Why this compiles, and does what it does without complianing is beyond me. I would expect it to complain the same way this does...

      #!/usr/bin/perl -w use strict; sub mycode { my $var = $_[0]; my $foo = "blah" if $var; print "foo is $foo\n" if $foo; $bar ||= "BAR"; } mycode("test"); mycode(); mycode(); mycode();
        It looks like you are absolutely right. The output of perl -MO=Deparse,-p a.pl follows (after removing Data::Dumper):

        sub mycode { (my $var = $_[0]); (defined($var) ? print("mycode() called with $var\n") : print("mycode() called with no operands.\n")); ($var and (my $foo = 'blah')); #^^^^^^^^^^^^^^^ Look here ($foo and print("foo is $foo\n")); ($foo ||= 'FOO'); } mycode('test'); mycode(); mycode(); mycode(); a.pl syntax OK

        my $foo = 'blah' is optimized away.

        In order to make it behave as expected, my $foo; needs to be its own statement, with no conditional.

        Update: Removed bit about strict, see the follow up

      I'm not sure that quite works. In theory, on the first call, my $foo = "blah" should be run, then go out of scope at the end of the function call. So, if the declaration/assignment is not run on the second call, shouldn't $foo ||= "FOO" give us an error?


      _______________
      D a m n D i r t y A p e
      Home Node | Email
        I vaguely recall reading something about the duality of run-/compilation timeness of my causing this. Unless I'm off by several orbits, the cause was that the scope is determined at compilation time, but the actual scalar is allocated at runtime, and is the same thing as causes $bar to persist in things like
        sub foo { my $bar = 0; sub baz { print $bar++; } }
        (You know the drill - Variable "$bar" will not stay shared.)

        Makeshifts last the longest.

        The reason we cant give an error is because my $foo = "blah" only gets executed if $var is true. However, we dont actually know if $var is true or not until runtime.

        So when you call mycode() with no arguments then my $foo = 'blah' is not executed at all. Instead, simply $foo = 'FOO' gets executed, setting $main::foo (you can verify this by printing out $main::foo after you call mycode() with no arguments).

        use strict is only supposed to catch compile-time errors (according to its documentation). It should not blow up at runtime.

        To do what you expect here you could do something like:

        
            my $foo = defined $var ? 'blah' : 'FOO';
        or even:
        
            my $foo;
            if ($var) {
                $foo = 'blah';
            }
            else {
                $foo = 'FOO';
            }