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

Hi Monks,
I would expect the $var lexical variable to be undef on each iteration in the following code snippet, where as it's not the case. The following example will print:

----
Use of uninitialized value in concatenation... 555 555
----
for (1..3) { my $var = '' if 0; print STDERR "$var\n"; $var = 555; }

My question is: is this "by design" for the lexical variable defined in this way, to keep the value from previous iteration. Shouldn't the "padlist" be cleared on each iteration?

20071101 Janitored by Corion: Added formatting, code tags, as per Writeup Formatting Tips

Replies are listed 'Best First'.
Re: Lexical scope variable is not undef'ed
by FunkyMonk (Bishop) on Oct 22, 2007 at 16:37 UTC
    my $var = '' if 0 creates a static variable (ie one that retains its value through successive iterations of your loop) in the version of perl you are currently using. Generally, the action of the construct you've used is undefined and, as such, shouldn't be used.

    In short...

    DON'T DO IT

    Update: Here's what perlsyn has to say about it:

    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.

Re: Lexical scope variable is not undef'ed
by ikegami (Patriarch) on Oct 22, 2007 at 16:46 UTC

    The pad entry for $var is cleared by a directive placed on the stack by my at run-time. Since you never execute the my at run-time, the directive is never created, so $var is never cleared.

    The "trick" relies on undocumented internal implementation details, thus its use is discouraged. If you wish to create a "static" variable, then use

    { my $var; sub func { ... } }

    (Outer curlies optional.)

      And the code to do this in 5.10 will be

      sub func { state $var; }

      but I know you know I knew you knew this already :)

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

        I didn't know, which also means you didn't know I know you knew I knew this already. :)
Re: Lexical scope variable is not undef'ed
by Anonymous Monk on Oct 22, 2007 at 17:44 UTC
    Thanks for very helpful replies. The bottom line is that an obvious shortcut (my $var = 'fff' if $predicate;) for proper:
    my $var;
    $var = 'fff' if $predicate;

    Can be a tricky bug to track in say mod_perl environment, since it's behavior is not completely defined while no compiler warnings are produced...
      Ironically a warning was added at one point for this..then was removed because it triggered too many error messages.

      Grrr...

Re: Lexical scope variable is not undef'ed
by gamache (Friar) on Oct 22, 2007 at 16:46 UTC
    (Update: I am misled -- ignore the following)

    Your problem is this statement:

    my $var = '' if 0;
    Which is being parsed as:
    if (0) { my $var = ''; }
    ...And in turn, the statement is skipped entirely. Therefore you are never creating a lexical variable $var -- you are implicitly using a local variable $var, which retains its value from iteration to iteration and then resets itself once the loop is exited.

    perlsyn explains it.

    Example:

    bash$ perl -e 'for(1..3){my $i if 0; print $i++, "\n"}' 0 1 2 bash$ perl -e 'for(1..3){my $i if 1; print $i++, "\n"}' 0 0 0

      That's not true. It's not parsed as if (0) { my $var = ''; }, and it does create a lexical $i (my's compile-time effect).

      >perl -le "$::i=q{Pass }; for(1..3){my $i if 0; ++$i; print $::i, $i}" Pass 1 Pass 2 Pass 3 >perl -le "$::i=q{Pass }; for(1..3){ ++$i; print $::i, $i}" 11 22 33

      What happens is that the clearing doesn't occur at the end of the scope (my's run-time effect). See Re: Lexical scope variable is not undef'ed.