in reply to Uses for an lvalue subroutine

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")

Replies are listed 'Best First'.
Re: (tye)Re: Uses for an lvalue subroutine
by gildir (Pilgrim) on Jan 10, 2001 at 14:55 UTC
    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")
        I dont think so.
        Look at my example in previous reply. In SuperClass, attributes are stored in blessed hash, that means in object itself. But now I want to write a 'persistent' object, and I want attributes to be stored in some kind of persistent storage (database) instead of hash.
        I suppose SuperClass has many methods, all of them deal with $self->foo like a lvalue, using $self->foo = 123;.

        And now, how to write SubClass that will store foo attribute in database? If I have 'traditional' $self->foo(123) method, solution is trivial. But with lvalue methods I cannot do such a thing.

        It is never a good practice to make any assumption about storage of object attributes in strict OOP. For this reason we use $self->foo instead of $self->{foo}.

      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

        For me, I found that the lvalue allows me to implement accessor functions in a more intuitive manner, such as in the ring memory model, below.
        # ------------------------------------------------------- # Ring.pm : memory ring model for CW # Author : Velaki (velaki@aol.com) # Date : 09 July 2004 # Version : 1.0 # History : # # 1.0 09 July 2004 -- added init routines <Auth> # 0.2 09 July 2004 -- converted set/get to cell <Auth> # 0.1 08 July 2004 -- initial version <Auth> # # ------------------------------------------------------- package Ring; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw//; @EXPORT_OK = qw//; $VERSION = 1.0; use strict; my $DEFAULT_SIZE = 10; # Default ring size for memory ring. # ------------------------------------------------------- # Name : new # Desc : create a new memory ring # # Input : size to make memory ring, or none # Output: new memory ring segment # sub new { my $this = shift; my $class = ref($this) || $this; my $size = shift || $DEFAULT_SIZE; my $self = { SIZE => $size, RING => [] }; bless $self, $class; $self->init; return $self; } # ------------------------------------------------------- # ------------------------------------------------------- # Name : init # Desc : blank the ring memory # # Input : none # Output: none # sub init { my $self = shift; $self->cell($_) = 0 for (0 .. $self->size); } # ------------------------------------------------------- # ------------------------------------------------------- # Name : size # Desc : get the size of the ring # # Input : none # Output: the size of the ring # sub size { my $self = shift; return $self->{SIZE}; } # ------------------------------------------------------- # ------------------------------------------------------- # Name : cell # Desc : get or set the value of a ring cell # # Input : address to be set/retrieved # Output: the current value at the address # sub cell : lvalue { # making it an lvalue means # we can set it during # retrival, e.g. # $c->cell(3) = 10; # print $c->cell(3); # prints: 10 my $self = shift; my $addr = shift; # the address is normalized before being used $self->{RING}[$self->naddr($addr)]; } # ------------------------------------------------------- # ------------------------------------------------------- # Name : naddr # Desc : Normalize ring address to a positive number. # # Input : signed integer address # Output: unsigned address guaranteed to fit in ring # sub naddr { my $self = shift; my $addr = shift; return $addr % $self->size; } # ------------------------------------------------------- 1; # end of module
        This way, I can grab a ring instance, and use the accessor functions as follows:
        use Ring; my $r = Ring->new(); $r->cell(4)="142"; print $r->cell(4),"\n";
        This way, I don't have to think so much about accessing this particular member function.

        I hope that it remains a permanent feature of the language. -v.

Re: (tye)Re: Uses for an lvalue subroutine
by Hot Pastrami (Monk) on Jan 10, 2001 at 04:40 UTC
    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