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

Greetings wise monks,

I guess I've always assumed that beyond the conditions documented in perldoc perlsub an lvalue subroutine was treated as the modifiable value it represents.

For giggles I was messing around with local and lvalue subroutines and I'm wondering why a scalar accessed via an lvalue subroutine value seemingly can't be localized?

A quick look with Scalar::Util::refaddr tells me after the local() call I am indeed dealing with the same address still.

#!/usr/bin/perl use strict; use warnings; package Foo; our $level = 1; sub level :lvalue {$Foo::level} package main; print $Foo::level; { local Foo->level = 2; # Doesn't localize. # local $Foo::level = 2; # Does localize. print $Foo::level; } print $Foo::level; #Expected: 121 #Result: 122
Regards,
Shane.

Replies are listed 'Best First'.
Re: Localize lvalue subroutine.
by shmem (Chancellor) on Aug 06, 2007 at 10:09 UTC
    Localize the package global first, and then call the lvalue sub:
    package Foo; our $level = 1; sub level :lvalue {$Foo::level} package main; print $Foo::level; { local $Foo::level; # scalar localized... Foo->level = 2; # ...works on localized scalar print $Foo::level; } print $Foo::level; __END__ 121

    I read local Foo->level = 2 as trying to localize an expression (the lvalue sub itself?), and not the package global this sub operates on.

    A quick look with Scalar::Util::refaddr tells me after the local() call I am indeed dealing with the same address still.
    Hmm? $Foo::level is no reference - calling Scalar::Util::refaddr on it yields undef.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Localize the package global first, and then call the lvalue sub.

      Thanks for your time but I've confused the issue a little by using a variable accessible from the current scope. I was specifically interested in why the particular corner I've backed myself into doesn't work.
      { my $foo = 1; sub level : lvalue {$foo} } print level(); { local level() = 2; print level(); } print level(); __END__ 122

      I read local Foo->level = 2 as trying to localize an expression (the lvalue sub itself?), and not the package global this sub operates on.

      Ah that's interesting, after reading your reply I've run B::Deparse on my new example (sans our'd package variables) and the local() is removed altogether:
      { my $foo = 1; sub level : lvalue { $foo; } ; } print level(); { level() = 2; print level(); } print level();


      Hmm? $Foo::level is no reference - calling Scalar::Util::refaddr on it yields undef.


      I did \ my lvalues first :)

      My testing methods may be unorthodox but it seemed like a simple way to asses if the local() had happened like I expected and the localized Scalar::Util::refaddr \$Foo::level; now pointed at a different address.

      Presumably because perl has to create a new SV slot on the scratchpad for the inner scope the SV in question will have a different address when localized assuming everything goes well (I think I've got the lingo right?).

      Cheers,
      Shane.
        I was specifically interested in why the particular corner I've backed myself into doesn't work.
        { my $foo = 1; sub level : lvalue {$foo} } print level(); { local level() = 2; print level(); } print level(); __END__

        Because of my - it's a closure - and, as stated in perlsub

        A "local" is simply a modifier on an lvalue expression.

        No matter whether you localize the lvalue expression successfully or not, it is not the variable the lvalue sub operates on which is localized. An lvalue sub results in an lvalue upon which the operation in question is performed at subroutine return, and on the left-hand-side of the lvalue sub call you find the result of that operation. So, I guess you are effectively localizing the subroutine's return value, which isn't what you expected.

        In your code above, $foo is a lexically scoped scalar, and your subroutine is a "closure" - it closes over the scalar, which furthermore it isn't visible anywhere but in that scope, and as such can't be localized. Were you successfully operating with local upon that lexical variable, perl would have died with

        Can't localize lexical variable $foo at __FILE__ line __LINE__.

        Remember that my is a compile time directive, while local is runtime (see my/local, space/time (was: Re: The difference between my and local)).

        To localize a subroutine proper, you have to assign a coderef to its typeglob (*foo)

        { local *foo = sub : lvalue { print "wuff: $foo\n"; $foo }; }

        to make the localization effective (I guess it is optimized away otherwise, but I haven't checked that).

        Try to work out what is happening here:

        $\ = "\n"; $foo = 3; sub foo :lvalue { $foo } { local *foo = sub :lvalue { $foo *= 1.25; $foo; }; { local $foo; print '1: ',(foo() = 4); print '2: ',(foo() *= 4); foo() = 2; local *foo = sub :lvalue { local $foo = $foo * 1.25; $foo; }; print '3: ',(foo() *= 4); } local $foo = 4; print '4: ',(foo() /= 2); print '5: ',foo(); } print '6: ',foo(); __END__ 1: 4 2: 20 3: 10 4: 2.5 5: 3.125 6: 3

        All that said, an attempt to localize an lvalue sub with

        local foo() = 2;

        should result at least in a warning as

        Useless localizing of a subroutine return value

        or the like.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}