http://www.perlmonks.org?node_id=665045

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

Localizing a variable (and yes, I want local, not my) defined in a different package yields strange results:
####################### package A; our $variable = 3; ####################### package main; { # I want to localize the variable in this block local $A::variable; print "$A::variable\n"; } print "$A::variable\n";
The first print complains about an uninitialized value, while the second one prints the previously assigned value. Or, more generally speaking, how come that
package B; local $A::variable;
undefs $A::variable?

Replies are listed 'Best First'.
Re: What's wrong with this local() ?
by BrowserUk (Patriarch) on Jan 30, 2008 at 02:31 UTC
Re: What's wrong with this local() ?
by friedo (Prior) on Jan 30, 2008 at 02:33 UTC

    Because that's what local does! It lets you temporarily re-use the name of a package variable declared elsewhere.

    Consider this expanded example:

    #!/usr/bin/perl use strict; use warnings; package Foo; our $x = "blah"; package Bar; print $Foo::x, "\n"; { local $Foo::x; print $Foo::x, "\n"; $Foo::x = 42; print $Foo::x, "\n"; } print $Foo::x, "\n";
    which outputs...
    blah Use of uninitialized value in print at loc.pl line 15. 42 blah

    The first print shows the original value of $Foo::x, the second one throws an unitialized value warning because the localized version of $Foo::x has not been assigned a value. The third print gives 42 because we assigned that to the temporary $Foo::x, and the last one prints "blah" again, because the scope of the local has ended.

Re: What's wrong with this local() ?
by Fletch (Bishop) on Jan 30, 2008 at 02:32 UTC

    Erm, because when you localize a variable it sets its value to undef? The docs for local used to say this, but they don't mention it specifically any more. Probably because they presume anyone using it has read the other referenced docs.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: What's wrong with this local() ?
by ikegami (Patriarch) on Jan 30, 2008 at 03:05 UTC
    local $var;

    is functionally equivalent to

    my $saved_var = $var; ON_SCOPE_EXIT { $var = $saved_var; } $var = undef;

    except local can be used in the middle of an expression (where it returns its arg as a lvalue).

    Perhaps you want

    local $A::variable = $A::variable;

      Actually, it is closer to:

      # Save a reference to the original variable: *nothingToSeeHere::var= \$var; # Replace the variable with a new variable instance and assign # that new instance the given value (or undef if none mentioned): *var= \undef; # (but see footnote) # Restore the previous instance (and thus the previous value): ON_SCOPE_EXIT { *var= \$nothingToSeeHere::var; }

      That is, it is not just the value that is saved, but the scalar that holds that value. This can matter if the variable was tied or has other "magic" attached to it. It also matters if you have taken a reference to the original instance of the variable or take a reference to the new instance (they will point to different variables). It also means that the string value is not copied, as would happen with $saved= $var;.

      - tye        

      * Actually, this step is less like *var= \undef; (which leaves $var read-only) and perhaps more like *var= do { my $t= undef; \$t };. But testing shows that the latter leaves $var marked "PADBUSY" and "PADMY" so it is perhaps even more like *var= \[undef]->[0];. Go figure. :)

Re: What's wrong with this local() ?
by saintmike (Vicar) on Jan 30, 2008 at 04:13 UTC
    D'oh! D'oh! D'oh! Thanks everyone.
Re: What's wrong with this local() ?
by papidave (Pilgrim) on Jan 30, 2008 at 22:47 UTC
    Based on the question, I think you were expecting the localized version to be initialized to the external value. I ran a quick test under Perl v5.8.5, and found that

    local $A::variable = $A::variable;

    gives the desired behavior. It's nice, but it doesn't have to be that way, because this behavior implies that the "local" was executed after the RHS of the assignment was evaluated.

    Having said that, I don't think that precidence relationship is clearly defined in the language, and you probably shouldn't trust it for production code. Unless someone older and wiser than I knows for sure that it was planned that way?

      I think it may have been designed that way. When I look at the operator precedence table, I notice that the '=' is two spots higher in the table than list operators when viewed from the right (local). I also see that '=' evaluates it's right side 1st. So this seems to be a case of behavior matching documentation.

        When I look at the operator precedence table, I notice that the '=' is two spots higher in the table

        Precedence is irrelevant here. And local has higher precedence.

        It wouldn't work otherwise

        Give local higher precedence >perl -le"$x=2; { (local $x)=$x; print $x; $x=3; print $x } print $x" 2 3 2 Give assignment higher precedence >perl -le"$x=2; { local ($x=$x); print $x; $x=3; print $x } print $x" 2 3 3 <-- XXX

        I also see that '=' evaluates it's right side 1st.

        Where did you see this? Operand evaluation order determines which operand is evaluated first. The operand evaluation order for = and other Perl binary operators is not documented anywhere and should probably be considered undefined.

        Perhaps you looked up the associativity of the = operator. Associativity doesn't define which operand is evaluated first, it determines which operator is evaluated first when two operators of the same precedence are chained.

        However, you are right that this feature of my and local is implemented by choosing a right to left operand evaluation order for assignment operators.

        >perl -le"local(${print('a'), 'x'}) = ${print('b'), 'x'}; b a

        Updated: Updated code into "precedence is irrelevant" reply.

      By design, variables declared with my are not introduced (not visible) until after the current statement. For my, this is documented in perlsub along with the rest of my's documentation.

      Similar behaviour exists for local, although I can't find it documented.