in reply to Re: Are lvalue methods (as properties) a good idea?
in thread Are lvalue methods (as properties) a good idea?

It's relatively clean to make the sub a closure on a tied scalar, where the STORE() method does the checking. That succeeds in highjacking assignment.

I gave an example in To Validate Data In Lvalue Subs. That still seems to me like a clean and flexible approach to validation. As mentioned in that thread, Attribute::Properties uses this technique under the hood, but as you say the tie-in to an attributes interface reduces its applicability.

After Compline,
Zaxo

  • Comment on Re^2: Are lvalue methods (as properties) a good idea?

Replies are listed 'Best First'.
Re^3: Are lvalue methods (as properties) a good idea?
by Ovid (Cardinal) on Jan 12, 2005 at 22:56 UTC

    Wow. That's one heck of a lot of overhead to support syntactic sugar :) When you have to make the sub a closure and tie it to a custom class to provide validation for lvalue attributes, I'm at a loss to see how this is preferable to:

    sub bar { my ($self, $bar) = @_; croak "bar() requires a hashref" unless 'HASH' eq ref $bar; $self->{bar} = $bar; return $self; }

    Cheers,
    Ovid

    New address of my CGI Course.

      I'm at a loss to see how this is preferable to:

      Juerd has a node that demonstrated this nicely, but I cannnot find it right now. Compare:

      $obj->bar++; $obj->bar =~ s[a][b]g; $obj->len++ while substr( $obj->bar, $obj->start, $obj->len ) =~ m[^\s ++$]; substr( $obj->bar, $obj->start, $obj->len, ''); $obj->len = 0;

      with:

      $obj->bar( $obj->$bar() + 1 ); my $temp = $obj->bar(); $temp =~ s[a][b]g; $obj->bar( $temp ); my $temp2 = $obj->len; my $temp3 = $obj->bar; $temp2++ while substr( $temp3, $obj->start(), $temp2 ) =~ m[^\s+$]; substr $temp3, $obj->start, $temp2, '' ); $obj->bar( $temp2 ); $obj->len( $temp3 );

      Another way of looking at it. Would you prefer doing math like this?

      ## $a++; $a( $a() + 1 ); # $b *= $c-- $d++; $b( $b() * $c( $c() - 1 ) % $d( $d() + 1 ) );

      Examine what is said, not who speaks.
      Silence betokens consent.
      Love the truth but pardon error.
        I'd say that syntactic sugar is pretty sweet! The $obj->bar( $obj->$bar() + 1 ); thing is really ugly.

        I don't validate the contents of most of my getters/setters anyway, and for those I do, maybe I can live with the performance hit.

        What turns me away most is the future proofing, i.e. what if I some time want to something inside the setter that becomes difficult or ugly with the limitations an lvalue sub brings?

        All in all, I'm still in the undecided camp.

        /J

      I like the ability to use objects (and thus functions which return objects) as lvalues. A bit of my old C++ days sneaking up on me. But I won't use that until Perl6 when you can actually set a function to be the "set" routine, and a function as the "get" routine. I like the syntax of $obj->foo = 3, but I definitely need to be able to easily (that is, with little overhead) trap that to do something else with it - whether that's validate the new value, set a marker for change (that represents a dirty object for persistance), or send out events to listeners. Or even, heaven forbid, change my underlying object such that foo now represents something else. About the only thing I like about Perl 5's lvalue attributes is that they were something that taught Perl6's designers what was really needed :-)

      Sure it's overhead. But you only need to write the tieing class once globally, and you only need to write one validating closure per kind of validation. With that work out of the way, you can define the constraints for all your attributes declaratively.

      As far as I'm concerned it's a big win in terms of syntax and maintenance.

      Makeshifts last the longest.

Re^3: Are lvalue methods (as properties) a good idea?
by fergal (Chaplain) on Jan 16, 2005 at 15:02 UTC
    Class::Accessor::Lvalue also allows you to create them for a class and it has the nice feature that a call to $a->wibble = 5 gets translated to $a->set_wibble(5) so all your usual verification code is still callable (or you can choose to autogenerate a plain old setter with Class::Accessor).

    In my own code I use my own method maker module (not on CPAN because there are already enough method makers on CPAN) that does something similar but slightly unusual. Doing

    package Foo; use Fergal::MM qw( Bar );
    will create a setBar and getBar pair of methods and also a Bar lvalue method which just calls setBar and getBar. So far, so normal. What's unusual is that it puts the methods into Foo::Methods and makes Foo inherit from Foo::Methods so that if I want to have something special (validation etc) happen when a setter or getter is called. I just do
    package Foo; use Fergal::MM qw( Bar ); sub setBar { my $self = shift; my $value = shift; check($value); $self->SUPER::setBar($value); }
    Yes, there's plenty of overhead in this but usually it doen't make any difference to me.

    Another interesting thing I do with lvalue accessors is to add indexed accessors. So I can do something like

    $a->Grid(2, 3) = $player1; $a->Grid(7, 5) = $player2;
    this gets translated into
    $a->setGrid($player1, 2, 3); $a->setGrid($player2, 7, 5);
    so by writing the correct setters and getters, you can create attributes that act like multidimensional arrays or hashes and that are also overrideable by sub classes and can have validation etc.

      That is very cool. If you don't want to put this on CPAN, maybe you could talk to the author(s) of (an) existing module(s) and see if they wouldn't like to support for such?

      Makeshifts last the longest.

        The main reason for not putting it on CPAN is that I don't want to have to try and support other people's favourite method making features. I've been meaning to try get it into the one of the other packages but I've been away from home for a while and changing jobs etc. I might publish it as a proof of concept and invite others to steal any ideas that they like.