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

Hello, fellow Monks!

Please enlighten me on what's going on with the use of my combined with an if statement modifier on the 4th line. The output is very surprising. I expected the output to be "1\n2\n".

The value of @a is being retained between invocations of fn(). But if the stack frame for fn() is destroyed between calls, where is the value of @a being kept? If my is not having the effect of creating a local due to the if, then why doesn't the compiler complain that @a is an undeclared global?

perl -we ' use strict; sub fn { my @a = (7) if 0; push @a, @_; print "@a\n"; } fn 1; fn 2; ' 1 1 2

Replies are listed 'Best First'.
Re: Curious result of using "my ... if ..."
by bobf (Monsignor) on May 16, 2007 at 04:18 UTC

    See perlsyn, specifically the section on Statement Modifiers:

    NOTE: The behaviour of a my statement modified with a statement modifier conditional or loop construct (e.g. my $x if ...) is undefined. The value of the my variable may be undef, any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.

      Thanks! I couldn't find it before, but I see it now in perlsyn.
Re: Curious result of using "my ... if ..."
by Errto (Vicar) on May 16, 2007 at 04:53 UTC

    This is the ever-popular "static local variable" thing. The variable really is local - if you try to access @a outside fn you will get an error.

    The explanation, as I understand it, is that the action of the my in creating the variable is a one-time (compile-time, in fact) action so you get the same variable every time. But the assignment part is run-time and is skipped because of the modifier. This doesn't quite make sense to me, though, when you think about recursion, closures, etc.

    IMHO it would be nice to have an explicit feature for this, but it seems unlikely in Perl 5. Meanwhile, you can do the equivalent on your own:

    { my @a; sub fn { # code that handles @a } }

Re: Curious result of using "my ... if ..."
by perrin (Chancellor) on May 16, 2007 at 04:36 UTC
    It's a pretty well known bug. Perl 5.10 throws a warning for that type of construct.
      5.10 throws a warning for that type of construct

      No it doesn't. I seem to recall that blead did, at some point, but it appears to no longer do so.

      It has changed the behaviour, now the assignment just becomes undefined, but the previous value is not recovered from the pad.

      #! /usr/local/bin/perl5.9.5 use strict; use warnings; a(10); a(0); a(20); sub a { my $num = shift; my $x = $num * 100 if $num; print $x, $/; } # produces # 1000 # Use of uninitialized value $x in print at ./myif line 13. # 2000

      Real state variables are now available with the state declarator:

      sub c { state $count = 10; return $count++; } print c(), $/; # 10 print c(), $/; # 11 print c(), $/; # 12

      • another intruder with the mooring in the heart of the Perl

        5.10 throws a warning for that type of construct

        No it doesn't. I seem to recall that blead did, at some point, but it appears to no longer do so.

        I constantly wonder why it is not an error at all. Whatever its behaviour could be in an actual implementation, I can see no logical nor intuitive semantics that could be attached to the construct. It has a meaning to do something upon a condition. How can have a meaning for a thingie to exist or not, upon a condition? Point is, declaration happens at compile time, condition is verified at runtime. "Ideally" one could expect that in statements following

        my $var=whatever if condition;

        $var would be the lexical one just declared if condition is true, and something else (as if the declaration just were not there) otherwise: clearly, any way you look at it, it's unsatisfactory.

      Not so much a bug as an unmapped and(possibly) unmappable area of the Perl landscape. What is the sensible behaviour? Personally I think an error is appropriate.


      DWIM is Perl's answer to Gödel
        In my opinion, the sensible behavior, and what most people expect when they use it, is that it would operate the same as if the my($x) was on a separate line. If that isn't possible, it should throw an error.