in reply to Re^5: Perl OO and accessors
in thread Perl OO and accessors

As Ovid once put it:

Of course, some lvalue fans argue that I can use tie to get my validation back. Great! Now I've lost much of the conciseness I've hoped to gain and I've probably moved the data validation code away from the method in which the maintenance programmer expects to see it but at least I've switched to a slow, confusing and fragile interface!

-sauoq
"My two cents aren't worth a dime.";

Replies are listed 'Best First'.
Re^7: Perl OO and accessors
by fergal (Chaplain) on Nov 29, 2005 at 11:42 UTC
    Ovid's beating a straw man in that quote. Nobody is suggesting that everyday you should bash out lots of awful code directly involving tieing. Would yourself and Ovid also object to exporting values and functions because it involves advanced symbol table poking? Of course not, it's all hidden behind Exporter. Most users don't know how it works at all.

    The same goes for lvalue+tie. As I said in another post there's Class::Accessor::Lvalue. Personally I use my own home brew module which lets me say

    package Circle; use Fergal::MM qw( Radius Area ); # this creates # setRadius, getRadius, Radius # setArea, getArea, Area # # All are created in Circle::Accessors and Circle's @ISA # is updated to include Circle::Accessors. # This makes overriding easy. # now override the Area ones sub setArea { my ($self, $area) = @_; $self->Radius = sqrt($area/PI); } sub getArea { my ($self, $area) = @_; my $r = $self->Radius; return PI * $r * $r; }

    This removes all but one of Ovid's complaints - it's probably slow but if you really need speed then you can just ignore the lvalue interface and use the set/get methods directly. I think I benchmarked it as a factor of 10 slower but how much of your code is accessor calls? If you really need speed, Perl OO is probably not what you want.

      My summary: lots of needless complexity for little or no real gain. I object to that. Strongly. It's not good enough that you can do it. Why would anyone want to? I don't agree, by the way, that what you've shown removes "all but one of Ovid's complaints" but say we take that to be the case. What about the remaining issue? Why would I want to leave bits of a redundant API around with the caveat that, if performance is an issue, they shouldn't be used?

      -sauoq
      "My two cents aren't worth a dime.";
      
        lots of needless complexity for little or no real gain.

        First off, this is only needlessly difficult in Perl. Python, Ruby and many others support this kind of smart attribute out of the box.

        As for the gain, it's in programmer time, readability, simplicity, conciseness and uniformity.

        An attribute is essentially a variable with possibly some runtime enforced restrictions so it should be accessible just like any other variable. I can do

        $radius *= 2; $h{radius} *= 2; $radii[5] *= 2;
        so why should I have to do
        $c->setRadius($c->getRadius * 2);
        why not
        $c->Radius *= 2;
        ?

        The existence of something that fundamentally is a variable but that must be accessed in an unnatural way is actually a source of extra complexity. Accessors and mutators are something that are only necessary when the language designer didn't bother include smart attributes.

        A variable hidden behind methods causes more code to be written by anyone trying to use that variable and it actually prevents code reuse. For example any function which takes a reference to a variable and alters it can not be resued if the interface for the variable is via an accessor method. Whereas an lvalue attribute will work just fine anywhere a normal variable would have worked.

        Occam's razor says we should reduce the number of concepts we have to deal with, lvalue accessors mean we can throw away the awkward not-quite-a-variable attributes. Lispers realised this long ago and introduced generalised variables which give a uniform interface to "cars and cdrs of lists, elements of arrays, properties of symbols, and many other locations are also places where Lisp values are stored".

        Why would I want to leave bits of a redundant API around with the caveat that, if performance is an issue, they shouldn't be used?

        I think this is backwards. The Area method is the primary and recommended interface. setArea and getArea are the redundant parts of the interface (although they are an essential part of the implementation) and are available in case performance is an issue (they might also be useful if other code is expecting a setter method rather than an lvalue).

        So essentially, while the implementation may be complex, it is once-off complexity, hidden even from the author of the class and results in an overall reduction of complexity and increase in productivity. If it leads to unacceptable performance issues you can choose to use to the use the more complex interface in performance sensitive places in which case you still better off than when you were using set/get everywhere.

Re^7: Perl OO and accessors
by Aristotle (Chancellor) on Nov 29, 2005 at 06:21 UTC

    C’mon now, what do you expect me to say? Don’t use Perl5 OO? :-)

    Wait. Perl6 is right around the corner.

    Or maybe… Have you heard of Ruby?

    Seriously, though, things like Tie::Constrained take most of the pain away. Sure, I’d rather there was a better way, too. In any case, as a user of APIs with lvalue accessors I quite liked them; I haven’t so far actually designed an API with lvalue subs myself, however.

    Basically, the ugliness involved in lvalue accessors is just one more thing that makes me go “okay, I have appreciated the beauty and elegance in Perl5’s ultra-minimal OO approach enough, but now I’d like to get some real work done.”

    But all these considerations apply to the hidden end of an API. From the public side, lvalue accessors have no downsides whatsoever. They don’t expose anything untoward. That’s what I was arguing.

    Makeshifts last the longest.