in reply to (tye)Re: Uses for an lvalue subroutine
in thread Uses for an lvalue subroutine

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.

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

    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}.

        Oh, I see what you are complaining about now.

        But I disagree that inheritance should generally be used to override how an object's members are stored. I don't advocate using get/set methods to access members when within the object's methods, unless you specifically designed those members to be overridden. But I agree with your point that if you've made that decision, then using the current implementation of :lvalue subs might be something to avoid.

        I'd also avoid updating the RDMS for every single attribute assignment, caching the attributes in a hash and then flushing to the DB at destruction time, when requested, and/or at designed-in synchronization points.

        Using a tied reference (like merlyn suggests) crossed my mind but it seem like a pretty big hammer to use (quite a bit of overhead and coding required).

        An alternate work-around is:

        my %TempMap; sub foo :lvalue { my $self= shift; my $value= $self->fetch("foo"); $TempMap{"".\$value}= [$self,"foo"]; return bless My::Module::Value, \$value; } sub My::Module::Value::DESTROY { my $svValue= shift; my( $self, $name )= @{ $TempMap{$svValue} }; $self->store( $name, $$svValue ); }
        Which gets me to wondering how inheritance of :lvalue methods could be improved to avoid using tie to separate the code for fetching from storing (because I'd really like to provide a :lvalue interface to my members).

                - tye (but my friends call me "Tye")
Re: Re: Re: Uses for an lvalue subroutine
by merlyn (Sage) on Jan 10, 2001 at 21:40 UTC
    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.