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

Hi

I spotted a bug today in a large parallel/batch compute pipeline, which I managed to boil down to this 1 liner:

>perl -we 'use strict; sub test{ my $i = shift; my $params = shift; my $test1 = $params->{test} if exists $params->{test}; print "test1 after hash assignment:\t\t$test1\n"; $test1 = $i if ! defined $test1; print "i = $i\ttest1 after defaulting to i:\t$test1\n";} for my $i(0..3){ &test($i,{});}' Use of uninitialized value $test1 in concatenation (.) or string at -e + line 1. test1 after hash assignment: i = 0 test1 after defaulting to i: 0 test1 after hash assignment: 0 i = 1 test1 after defaulting to i: 0 test1 after hash assignment: 0 i = 2 test1 after defaulting to i: 0 test1 after hash assignment: 0 i = 3 test1 after defaulting to i: 0
(I added some line spacing in there for readability, so it may need reformatting to get it to run)

It seems to me like the joint declaration and assigned of the my $test1 variable is causing it to inherit the value from the previous call of the test subroutine. Rather than defaulting to the value of $i, and hence incrementing. Having been a perl developer for 10 years, this scope creep goes against all my understanding of locally scoped variables.

Am I doing something dumb here?

Thanks in advance.

Nath

Replies are listed 'Best First'.
Re: Assigning from post fix if exists breaks local scoping?
by kcott (Archbishop) on Mar 28, 2014 at 18:56 UTC

    G'day Nath,

    Welcome to the monastery.

    Perhaps have a chat with Robert who asked the same thing 27 minutes before you.

    Curiously, he also managed to boil it down to exactly the same code as you did.

    See "postfix syntax enlightenment".

    -- Ken

      Hmm,
      It seems like my code 'leaked' out of the office. Although the solution still does not address why the scoping is broken. Time for a little light reading methinks.

      Thanks for the redirect.

      Nath

        This issue was also recently raised on P2P/RT, here. Also:

        $ perl -wMdiagnostics -e 'my $x if 0' Deprecated use of my() in false conditional at -e line 1 (#1) (D deprecated) You used a declaration similar to my $x if 0. There has been a long-standing bug in Perl that causes a lexical variable not to be cleared at scope exit when its declaration includes a false conditional. Some people have exploited this bug to achieve a kind of static variable. Since we intend to fix this bug, we don't want people relying on this behavior. You can achieve a similar static effect by declaring the variable in a separate block outside the function, eg sub f { my $x if 0; return $x++ } becomes { my $x; sub f { return $x++ } } Beginning with perl 5.9.4, you can also use state variables to have lexicals that are initialized only once (see feature): sub f { state $x; return $x++ }