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

Hi all, I recently found out that perl allows subroutines to be lvalues. Which strikes me as very weird. So what can you use this feature for? One use that I came up with is as a callback-modified error routine:
sub error :lvalue { $error_string; } error() = "This is error 1\n"; while(1) { print error(), "\n"; # Code somewhere else assigns a new value # to error() (perhaps in a callback?) # The next time error is called, it prints # a different value. }
So what other uses are out there? What can be achieved with an lvalue subroutine that can't be achieved with a variable? Admittedly, TMTOWTDI...

Replies are listed 'Best First'.
(tye)Re: Uses for an lvalue subroutine
by tye (Sage) on Jan 10, 2001 at 04:07 UTC

    As merlyn demonstrated but didn't explain, the lvalue attribute for subroutines was mainly (from my perspective) motivated by the desire to have more traditional object members rather than force the writing of get/set methods for each member.

            - tye (but my friends call me "Tye")
      But if object method returns lvalue, it cannot be effectively overriden by subclasses to do something else.
      Example:
      package SuperClass; sub foo :lvalue { my $self = shift; $self->{foo} } package SubClass; @ISA=qw(SuperClass); sub foo :lvalue { # Now I want to store value of foo attribute in RDBMS, # how can I do that? }
      In this context, lvalue object methods are very dangerous, because they limit flexibility of objects.

        You use $self->{foo}, of course.

        When using inheritance, you don't treat your ancestors as black boxes (well, if you are doing inheritance of APIs, then you do, but Perl doesn't natively support inheritance of APIs anyway).

        Of course, if the SuperClass::foo() is very complex or you just want to write an inefficient subclass, then you can use $self->SUPER::foo() instead.

        So using :lvalue methods in a class doesn't really change anything about inheritance.

                - tye (but my friends call me "Tye")
        Return a tied object as an lvalue. Here's a quick sample using a DBM:
        dbmopen %FOO, "my_database", 0644 or die; ... sub foo :lvalue { my $self = shift; $FOO{$self->key}; } ... my $obj = SubClass->new; $obj->foo = 35; # calls tied(%FOO)->STORE($obj->key, 35); my $fetch = $obj->foo; # calls tied(%FOO)->FETCH($obj->key);
        Change the tie package as needed.

        -- Randal L. Schwartz, Perl hacker

      My Second Edition Camel book appears to have no mention of this feature... is it new to Perl 5.6, or just one of those details left out of the Camel? I suppose that the third possibility would be that it is in the Camel and I was too dumb to notice, but let's not talk about that.

      It looks pretty darn useful though, as merlyn illustrated... I'm surprised that I've never heard of it before.

      Hot Pastrami
        Second camel is 5.002, and yes, it's not in 5.2. Third camel is 5.6, and yes, it was added between 5.5.3 and 5.6, which means I'm not touching it for production code yet. However, 5.6.1 is just around the corner, and I'll finally be able to upgrade (yeay!).

        -- Randal L. Schwartz, Perl hacker

Re: Uses for an lvalue subroutine
by merlyn (Sage) on Jan 10, 2001 at 04:01 UTC
      Oh, the joys of lvalue!
          package Object;
      
          use vars '$AUTOLOAD';
      
          # :
      
          sub AUTOLOAD : lvalue
          {
                  my ($self) = shift;
      
                  $AUTOLOAD =~ s/.*://;
      
                  @_? $self->{$AUTOLOAD} = shift : $self->{$AUTOLOAD} ||= undef;
          }
      
      Sweet candy. Now you can do stuff like this:
           my ($object) = new Object;
      
           $object->whatever = "Data";
           print "Whatever is '",$object->whatever,"'\n";
      
      It "goes both ways" as an LVALUE, and this AUTOLOAD routine has backwards compatibility with the older, somesay wacky function method:
           my ($object) = new Object;
      
           $object->whatever("Data");
           print "Whatever is '",$object->whatever(),"'\n";
      
      You even get warnings with perl -w if you use uninitialized members.
        That's twisted and clever. One question: what is the intent of the ||= undef, other than changing $self->{$AUTOLOAD} to undef if it's 0 or ""?
        my ($object) = new Object; $object->whatever(0); print "Whatever is '",$object->whatever(),"'\n"; # Oops!
      Good example, but I think you mean shift   :P

      -Lee

      "To be civilized is to deny one's nature."
Re: Uses for an lvalue subroutine
by lemming (Priest) on Jan 10, 2001 at 04:03 UTC
    You may want to look at substr for an example to start with.