Re: Method Chaining and Accessors
by mreece (Friar) on Apr 05, 2007 at 00:03 UTC
|
can't you just test for @_?
sub foo {
my $self = shift; # oops, forgot this line before
if (@_) {
$self->{foo} = shift;
return $self;
}
return $self->{foo};
}
updated to shift off self. thanks, Moron | [reply] [d/l] [select] |
|
| [reply] |
|
Almost, but because the object arrives in $_[0] either way, you need to shift it out before testing @_, e.g. ...
sub foo {
my $self = shift;
@_ or return $self -> {foo};
$self ->{foo} = shift;
$self;
}
| [reply] [d/l] |
Re: Method Chaining and Accessors
by f00li5h (Chaplain) on Apr 05, 2007 at 01:25 UTC
|
TheDamian suggests the use of get_foo and set_foo.This is because you have some attributes for which there will be a get_foo but no set_foo so the example was that a foo sub that is both a getter and setter has no way to indicate that you can not actually set foo, because if it dies then you have to wrap all your get/setters in eval { ... } and nobody wants that.
package Foo;
use Readonly my $SPAM_FACTOR = 22;
use Readonly my $SPAM_FUDGE = 42; # seasonally adjusted
# cause-cause-bundles-of-problems constructor
sub new { bless {spam=>4} , shift }
# you can get or set bar with $that->bar
sub bar {
my ($this, $newbar) = @_;
if (@_) $this->{bar} = $newbar;
return $this->{bar};
}
# you can't set baz, because it's derrived automatically from other at
+tribute
sub baz {
my ($this) = @_;
# just ignore @_ ... perhaps warn "you cant change baz"
return $this->{spam} * $SPAM_FACTOR + $SPAM_FUDGE;
}
# some time later
pacakge main;
my $foo = Foo->new();
$foo->bar( 'moose' ); # neat, bar is moose
$foo->baz( 'shoe' ); # not so much, worst of all, it looks right!
printf "I have a lovely foo, it has %s bar and %s baz " , $foo->bar, $
+foo->baz;
# hey! where's my shoe!?
if you use the get_bar and a set_bar convention, perl will complain bitterly when you call set_baz when the pacakge Foo does not provide one and you'll know straight away that you can't set_baz after all.
@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;
| [reply] [d/l] [select] |
|
TheDamian suggests the use of get_foo and set_foo.This is because you have some attributes for which there will be a get_foo but no set_foo so the example was that a foo sub that is both a getter and setter has no way to indicate that you can not actually set foo, because if it dies then you have to wrap all your get/setters in eval { ... } and nobody wants that.
A good point, but I think you can make the case even more simply: undef is a useful value, and sometimes you want to set something to undef. If you use undef to indicate you want to do a get rather than a set, then you're stuck.
So yeah, explicit getters & setters are good, and mutators aren't worth the saving of a few lines of code.
Myself, I'm inclined to name my setters "set_*" but to avoid using the prefix "get_" on my getters. I think that reads a little more naturally and helps to distinguish between the two:
my $attribute = $self->attribute;
$self->set_attribute( $attribute );
| [reply] [d/l] |
|
my $foo = Foo::Doom->new();
$foo->bar( 'shoe' ); # OH NOES! its just a getter
# but it still looks right -_-
# whereas
my $f00 = Foo::f00li5h->new( in_accordance_with => TheDamian );
$f00->get_bar( 'shoe' ); # what the hell is 'shoe' there for?
# this is clearly a getter
Sure if you've got a bundle of other setters called, as set_foo in exactly the same place that you're adding the new code to set bar, you're a little less likely to write $foo->bar('shoe'), but only a litte.
Personaly, I'd expect $foo->bar to be a mutator.
@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;
| [reply] [d/l] [select] |
|
|
|
|
sub foo {
my $self = shift;
# Set attribute if any args
if ( @_ ) {
$self->{foo} = shift; #validation is for chumps
return $self;
}
else {
return $self->{foo}
}
die "Whiskey Tango Foxtrot";
}
# cotrast with this version, which I think you had in mind
sub undefoo {
my $self = shift;
my $newfoo = shift;
if ( defined $newfoo ) {
$self->{foo} = $newfoo; # What, me validate input?
return $self;
}
else {
return $self->{foo};
}
die "Whiskey Tango Foxtrot";
}
print $obj->foo(undef), "\n"; # Prints object stringification
print $obj->undefoo(undef), "\n"; # prints a "\n"
I don't know how I feel about chaining mutators. I like accessor/mutators that always return the attribute value.
| [reply] [d/l] [select] |
Re: Method Chaining and Accessors
by friedo (Prior) on Apr 05, 2007 at 01:34 UTC
|
While chaining is neato and all, I can't think of a good reason to support this use. What's wrong with simply passing a hash of configuration information to your constructor? This seems far more reasonable to me:
my $obj = My::Class->new( foo => 'foo', bar => 'bar' );
Under the hood, your constructor can either modify the object directly or call the accessors/mutators and let them do the work.
| [reply] [d/l] |
|
The client can pass silly things in the hash, and the writer of the class has to validate that all required keys are present, and extranious ones cause warnings. unless you have Class::Std do it all for you.
(and to be pedantic, you're not passing a hash at all)
Update I forgot to mention that I did actually agree with the "passing data to the constructor" idea.
@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;
| [reply] [d/l] |
|
Class::Std is definitely worth a look-see. It automates a lot of the messiness you (the OP) are trying to avoid.
cat >~/.sig </dev/interesting
| [reply] |
|
While chaining is neato and all, I can't think of a good reason to support this use.
I can. Lots. There's many a time you can't stuff everything into a constructor, and then live with the object ever after. Sometimes you have to twiddle the object repeatedly during the life of the program. In that context, method chaining usually results in more compact, clearer code.
Take a look at this review of HTML::CalendarMonth, which is an example of where I think chained methods would be a big win.
• another intruder with the mooring in the heart of the Perl
| [reply] |
|
While chaining is neato and all, I can't think of a good reason to support this use. What's wrong with simply passing a hash of configuration information to your constructor?
Besides what grinder has already mentioned, there's an inability to force the order of the operations.
For instance, in SOAP::Lite, there are some settings (eg, 'service') that affect mutliple parameters, so I need to ensure that they're called first, and then the ones I want to individually override.
Now, in your case, you passed a list, so the method could just splice off the front and make sure they were applied in the correct order -- if you had used a hash, that wouldn't have been possible.
| [reply] |
[OT] Re: Method Chaining and Accessors
by crashtest (Curate) on Apr 05, 2007 at 01:10 UTC
|
This is a bit off-topic, but the first thing that popped into my head at the term "method chaining" was the common Smalltalk idiom for what you are trying to do:
| myObj |
myObj := SomeClass new method1: foo;
method2: bar;
yourself.
I always thought it was very elegant and concise. method1 and method2 can return whatever they want. The yourself then hands back the newly created object.
Of course in Smalltalk, you would have to write separate getter and setter methods (though you could overload the method name).
| [reply] [d/l] [select] |
Re: Method Chaining and Accessors
by linenoise (Acolyte) on Apr 05, 2007 at 04:46 UTC
|
Thanks for all your comments. It's been interesting reading! I'm a Unix system administrator by trade and even though I've been using Perl since the early 90s most of the time I'm just using it for quick and dirty hacks to get something working now. I relish the chance to do more advanced things with it, hence my question today.
I guess the main reason I wanted to use chained methods is because I've seen them used elsewhere and when it came to writing this current application I figured I'd try to figure them out for myself. Not because I particularly need them but just because I was curious. :-)
One of the things I love about Perl is that once you know the basics you can figure out how to do a lot of complicated things just by "guessing." So, in this instance I thought "I wonder if all I need to do is return $self to get chaining to work?" It did work but broke something else which is what prompted my question here.
I'll never forget when I found out about hashes of hashes. It turned out that I'd been using things like $foo{'bar'}{'baz'} for years because it just seemed logical to me to do it but I never realised that what I was doing actually had a name and that what I was really doing was storing an anonymous hash with a key of 'baz' in another hash with a key of 'bar'. Once I figured that out it opened up a whole world of complex data structures but it all stemmed from me just assuming that I could put multiple keys on a hash to create a multi-dimensional array and Perl would DWIM.
Anyway, that's just my longwinded way of saying thanks! I've been lurking here for a while reading people's questions and answers but this was my first question and even though it had a simple answer it has made me think a lot more about what I'm doing! | [reply] |
Re: Method Chaining and Accessors
by misterwhipple (Monk) on Apr 05, 2007 at 03:09 UTC
|
| [reply] |
|
I've had a play with C::R. While you can do really neat stuff with it, the problem is that others can do what they consider to be really neat stuff with it.
This is the same problem that's had with operator overloading in C++. Take the relatively innocuous statement i = j + k;. Do you know what's going on? Really? What would happen if I told you what classes i, j, and k were. Does that help? Oh, it doesn't?
99% of Perl coders doesn't understand basic list, scalar, and void contexts. Now, you're going to throw in a gazillion other contexts? Maybe not such a good idea.
Oh, and it imposes a 2-4x runtime overhead. (Note - I make this point last for a reason.)
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] [d/l] |
|
I remember reading mention of the performance hit. Dragonchild: Was your C::R experience before or after last month's release?
cat >~/.sig </dev/interesting
| [reply] [d/l] |
|
Re: Method Chaining and Accessors
by dragonchild (Archbishop) on Apr 05, 2007 at 03:33 UTC
|
Use Moose.
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] |
Re: Method Chaining and Accessors
by exussum0 (Vicar) on Apr 05, 2007 at 22:23 UTC
|
The bad thing about method chaining is debugging a line that's in a chain. You can step INTO a line, but if you don't know which part of the chain is busted before hand, you have a lot of chaining in and out. If one does something like...
$x->y();
$x->z();
$x->moo();
You're more likely to get an easier to diagnose report should something go odd. Also, it won't matter if y(), z() or moo() returned anything at all. It's a minor quibble that becomes defensive programming. | [reply] [d/l] |
Re: Method Chaining and Accessors
by talexb (Chancellor) on Apr 05, 2007 at 16:53 UTC
|
I wonder if instead of
my $obj = My::Class->new()->foo("foo")->bar("bar");
you couldn't do
my $obj = My::Class->new({ foo => 'foo', bar => 'bar'});
That would make things a little more clear than trying to decipher the layers of arrows. And you could also punctuate it differently ..
my $obj = My::Class->new({
foo => 'foo',
bar => 'bar'
});
.. depending on your taste.
Alex / talexb / Toronto
"Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds
| [reply] [d/l] [select] |