Chaining in Moose, Mouse, Moo and Mo
Recently, I created a new issue on Github for Mo (https://github.com/ingydotnet/mo-pm), suggesting a chained interface:
package Hello;
use Mo;
has 'first';
has 'second';
my $hello = Hello->new;
# current implementation
$hello->first('foo');
$hello->second('bar');
# result
print $hello->first; # foo
print $hello->second; # bar
# chaining (suggested)
$hello->first('foo')->second('bar');
The suggestion was rejected, providing the following reasons:
"Neither Moo nor Moose nor Mouse does this by default, and nor do the vast majority of pre-Moose accessor builders."
While this is in line with Mo guidelines
"Mo will attempt to not do the things it does in an incompatible style to the Moose family.",
the question is: why not switch to a chaining interface in Moose, Mouse and Moo, and Mo? Should this even become the default behaviour?
Have you considered a case when your accessors do fail?
this might be a valid point, however, you could still use the old api if this is a concern
$hello->first('foo');
$hello->second('bar');
Lots of time they return the actual value that was set (that in some cases can be different from the argument). Therefore, you can use it in conditionals, for instance, because the "set" value will be returned...
so question might be: what is the most common use case - chaining vs. modified setter values
this is probably what you mean:
$hello->first('foo');
if ($hello->first && $hello->first eq 'FOO') {
}
vs.
if ($hello->first('foo') && $hello->first eq 'FOO') {
}
If you want this specific behavior in Moose, there is a MooseX:: for that (see https://www.metacpan.org/module/MooseX::Traits::Attribute::Chained ).
As for why it is not default in Moose. To start with it is because the convention is to return the value set (not just in Perl, but in most languages (NOTE: jQuery is not a language and while its API is useful, it is a specific case and not something to be applied across the board)), and because chained accessors seem useful up until you begin to explore the edge cases.
The obvious one being the possible failure case (as komarov pointed out) and the more subtle one being the API complexity it creates. If an accessor always returns the value stored, no matter if it is get or set behavior, then it is very easy to predict behavior. And while your simple case of accessors that store strings might seem to save you some typing, a really application would store complex objects and in those cases you very well might want to call methods on those objects rather then have to do another get call to get at them.
I am not disagreeing that in some cases chained accessors might be useful, but you really don't want all your accessors behaving this way all the time. Now Moose gives you the power to pick and chose (again, see the MooseX:: I linked to above, it does that), but now you are creating even more API complexity because now some of your methods chain and some don't.
If you really want this, I suggest instead of trying to add to the core of Mo, you simply use the plugin API that ingy has created to write your own Mo extension (similar to what was done with the MooseX).
- Stevan
Additionally, changing the default behavior of Moose at this point would break the over 1400 CPAN modules which depend on Moose and that behavior. Not to mention the amount of DarkPAN code it would break, this kind of massive API change is not something that can even be considered.
And again, there is a MooseX for that, as there is for almost anything like this. Moose was built to be extensible in this way, so that if you didn't like the default, you could change it in a safe and confined manner.
- Stevan
Mm, is there a MooseX for that? :)
I like method/attribute chaining, but it also makes getting return value a pain. The MooseX strategy is smart here.
@Steven Little
thanks for your comprehensive explanation. I find myself using chained accessors all the time. This is why I was writing this post, as it is the most common use case for me :)
Wikipedia -->
http://en.wikipedia.org/wiki/Method_(computer_programming)#Accessor_and_mutator_methods
"An accessor method is a method that is usually small, simple and provides the sole means for the state of an object to be accessed (retrieved) from other parts of a program."
So chaining might work for me as I actually keep accessors simple all the time.
Besides JQuery, there is at least Mojo::Base which is also kind of a Moose Tiny with chained accessors.
package Tiger;
# replaces use base
use Mojo::Base 'Cat';
# simple read write accessor with default
has stripes => 42;
I will look at the Mo plugin API, however I am not sure whether I will understand the obfuscated source code :)
created a chaining plugin:
https://github.com/forwardever/mo-pm/commit/eb543b7d0e82a3ee498f427f34122c20da12b06c
not sure about the api, so it is just experimental