Re: Are lvalue methods (as properties) a good idea?
by Zaxo (Archbishop) on Jan 12, 2005 at 22:08 UTC
|
They're at their best when used with lexical closures. They make a very nice way to get subroutine-style scoping for class variables. That means they replace package globals with a slightly different scoping model. They have the advantage of being overridable and inheritable.
Lexical closures on lvalue subs are less handy for object methods. You need to define the sub in the constructor and keep a reference to it around. It looks like that is what the package you mention probably does. Used as an lvalue, a naive instance method finds it tricky to associate itself with the correct object, and if the instance method didn't see the object data's my call when defined, trouble can result.
I think that lvalue subs are stable to use for scalars. Afaik there are problems with list context for arrays and hashes. A fix for those is likely to break something.
Lvalue subs are tremendous fun to experiment on. Here's an example whose effects are enlightening to investigate,
my @crefs = do {
my $foo = \$_;
(
sub :lvalue { $$foo },
sub :lvalue { $foo }
)
};
| [reply] [d/l] |
Re: Are lvalue methods (as properties) a good idea?
by Ovid (Cardinal) on Jan 12, 2005 at 21:42 UTC
|
I'm not too fond of them. Currently, there is no clean way to validate the value:
$object->digits = [qw/finger finger thumb/]; # whoops!
One can use Attribute::Property for that, but then you're relying on a attributes and those rely on CHECK blocks and CHECK blocks don't run under all environments (mod_perl, for example). So if you want to use lvalues, feel free, but just be leery of using them in code that you distribute to others as they are rather fragile.
| [reply] [d/l] |
|
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.
| [reply] |
|
sub bar {
my ($self, $bar) = @_;
croak "bar() requires a hashref" unless 'HASH' eq ref $bar;
$self->{bar} = $bar;
return $self;
}
| [reply] [d/l] |
|
|
|
|
|
|
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.
| [reply] [d/l] [select] |
|
|
|
Re: Are lvalue methods (as properties) a good idea?
by Anonymous Monk on Jan 12, 2005 at 21:37 UTC
|
Getters/setters break encapsulation, so if you are never trying to do the equivalent of overriding = to call additional code, you might as well be saying $object->{digits}=123 for that matter... If the module allows overriding of property methods on a case by case basis, then good for it...
| [reply] |
|
| [reply] |
|
| [reply] |
|
|
It's not the getters and setters that ar bad, but using them to upset Mr Demeter.
See also: Re^4: Law of Demeter and Class::DBI
IMHO the Law of Demeter isn't like the speed of light, but it's a pretty good heuristic for OO design.
/J
| [reply] |
|
|
|
|
|
I never see anyone provide concrete references that really explain why getters and setters are such a horrible thing
Yes, I dislike emotional words like horrible and evil; writers employ these words as attention grabbers. As merlyn explains nicely, they are not always horrible or evil ... but can be if used unwisely. As usual, there are tradeoffs involved and mindless slogans cannot replace sound judgement, experience and good taste.
A couple of general references that discuss this are:
Allen Holub JavaWorld article Why getters/setters are evil
AccessorsAreEvil
TellDontAsk
| [reply] |
|
Definition of lvalue
by tphyahoo (Vicar) on Jan 13, 2005 at 10:43 UTC
|
I didn't know what lvalues were when I read this post, so I did a little research and found:
Definition of lvalue: A reference to a location, an expression which can appear as the destination of an assignment operator indicating where a value should be stored. For example, a variable or an array element are lvalues but the constant 42 and the expression i+1 are not.
http://www.google.de/search?q=cache:h6MRIOsNjKwJ:www.hyperdictionary.com/computing/lvalue+lvalue&hl=de
thomas. | [reply] |
|
That's a somewhat confusing defintion of lvalue. A lvalue, quite literally, is something that can appear on the left hand side of an = operator -- that is, something that can be assigned to. In perl, that includes assignable variables (read: almost all variables), and calls to lvalue subroutines (including lvaluable builtins like substr, and user-defined subroutines with the :lvalue attribute). (The first half of that includes, of course, any expression that results in an assignable variable, such as $foo->{bar}{baz}{quux}[-1].) The 42 in 42=1; is a non-lvalue in a slot that needs an lvalue. Note that there are places that need an lvalue other then the right-hand-side of a = operator, such as all the *= operators, ++ and --, and some sub arguments, sometimes only in some situations. In general, if an operation is going to modify something, that something must be an lvalue -- which is another defintion of lvalue: "something that can be modified".
Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).
| [reply] [d/l] [select] |
Re: Are lvalue methods (as properties) a good idea?
by perrin (Chancellor) on Jan 13, 2005 at 19:19 UTC
|
There may not be a good technical reason to stay away, but they look different from all other forms of method calls in Perl, which will make your code confusing to most other Perl programmers. It may also get you in the habit of writing method calls this way and that could cause you to make mistakes when using DBI, LWP, etc. It just adds to the cognitive dissonance. | [reply] |
Re: Are lvalue methods (as properties) a good idea?
by fletcher_the_dog (Friar) on Jan 14, 2005 at 15:33 UTC
|
My biggest problem with lvalue subs is that they break in the debugger. Subs are intercepted in the debugger by a sub called DB::sub that is not an lvalue sub. So you get weird bugs when a program with lvalue subs that normally works fine is run through a the perl debugger, because values stop getting assigned in the lvalue subs | [reply] |
|
| [reply] |
Re: Are lvalue methods (as properties) a good idea?
by blazar (Canon) on Jan 14, 2005 at 16:25 UTC
|
But I think I remember that lvalue subs are considered experimental. I super searched here, but didn't become much wiser.
I think lvalue subs have been deemed experimental for quite a long time, but they've been there and working for all this time. As far as your question is concerned: most times you don't really need them, but in a few cases they provide what indeed seems to be the most natural and straightforward syntax for certain tasks. So it's indeed a nice feature to have. granted, it would be better if that "experimental" tag could be eliminated...
PS: at least as of 5.8.6 the "experimental" mark is still there, see 'perldoc perlsub'.
| [reply] |
|
They will not be marked "experimental" in Perl 6. At least, no more experimental than Perl 6 itself...
| [reply] |