On Thu, Sep 30, 2010 at 08:51:04PM -0400, Ricardo Signes wrote:
> * Hans Dieter Pearcey <[email protected]> [2010-09-30T14:35:13]
> > We don't like the fact that lazy_build autogenerates public clear_$attr and
> > has_$attr methods for you. OK, I can get behind that; those often don't
> > belong as part of the "public" API, and yes, people will consider them
> > public
> > if they don't have a leading underscore, even if they aren't documented.
>
> I am really not excited about doing anything to remove lazy_build. I'd rather
> see something better introduced so that lazy_build is a lousy option. Let's
> talk about other options, and then the question of deprecating lazy_build or
> not is less interesting to me...
>
> We discussed this on #moose-dev for a while today, and one of the suggestions
> that seemed to get some general favor was this (mine):
>
> Allow anything that takes a method name to do a very, very simple expansion
> on the name, replacing some token with the attribute name.
>
> For the sake of discussion, we used $. I think that's also a tolerable thing
> to actually use, but I am not too worried about it.
>
> So, for example, these are equivalent:
>
> has foo => (builder => '_build_$');
> has foo => (builder => '_build_foo');
>
> This is a relevant change for the topic at hand because now I can write this:
>
> sub lazy_build {
> return (
> lazy => 1,
> builder => '_build_$',
> clearer => 'clear_$',
> predicate => 'has_$',
> );
> }
>
> has foo => (lazy_build, ...);
>
> (Read on, if you think that the non-pair item sucks, before complaining!)
>
> Another unrelated benefit is that we can get rid of the need for 'is' because
> we can:
>
> sub rw {
> return (accessor => '$');
> }
>
> Then, while you could always give a full name for attributes, you could also
> have these "policy" routines that return a set of methods to add. There's no
> need to make each one a trait, they work very well as pairs.
>
> ...but, as I noted above, the routine call breaks the pair-looking structure
> in:
>
> has foo => (
> lazy_build(),
> isa => 'Meta',
> );
>
> There were two suggestions, the first Sartak's, the second mine:
>
> # Sartak said, what about:
> sub rw_lazy_build {
> return ('rw', builder => '_build_$', clearer => 'clear_$');
> }
>
> has foo => (is => rw_lazy_build);
>
> I suggested:
>
> sub lazy_build {
> return { builder => '_build_$', clearer => 'clear_$' }
> }
>
> has foo => (
> policy => lazy_build,
> );
>
> "policy" (or, as I suggested at the time, "attr") would basically flatten the
> hashref given and use it as more args. If desirable, it could detect
> conflicts or allow overrides. "Your policy said clearer=>clear_$ but you also
> said clearer=>undef, so no clearer."
>
> Finally, I think the (builder => 1) is a good idea. I would implement it by
> saying that there is a attribute mapping method types to default strings, so
> the default string for a builder can be '_build_$' and for accessor '$' and so
> on.
>
> has foo => (
> accessor => 1,
> builder => 1,
> clearer => 'reset_$',
> ...
> );
>
> Policy traits can then just change the default strings, which would be quite
> convenient.
>
> I think there's a lot of power to be gained from this pretty simple change,
> almost no matter what parts of the suggestions we choose.
>
> P.S., we may want a second indicator for "_ iff attr name begins with _" for:
>
> clearer => '?clear_$'
>
> ...but any further special markers would be an abomination.
So, as I mentioned earlier in the conversation (before you joined I
think), this code already exists.
package Foo;
use Moose;
use MooseX::Attribute::Shorthand my_lazy_build => {
lazy => 1,
builder => sub { "_build_$_[0]" },
predicate => sub {
my $name = shift;
my $private = $name =~ s/^_//;
$private ? "_has_$name" : "has_$name";
},
clearer => sub {
my $name = shift;
my $private = $name =~ s/^_//;
$private ? "_clear_$name" : "clear_$name";
},
};
has foo => (is => 'ro', my_lazy_build => 1);
This works right now, although it's not released, as I'm still waiting
on a branch to get merged so that it can work in roles. If we decide
this should be core functionality, replacing "use
MooseX::Attribute::Shorthand" by some sugar function in Moose::Util or
something ('attr_policy'?) would be easy enough (and then we could just
implement 'is' and 'lazy_build' inside Moose::init_meta rather than
having the actual attribute code have to know about them.
In terms of functionality, I think that using coderefs here is a more
flexible solution - it sidesteps the "how do I get an optional leading
_" issue, and it doesn't require parsing strings with special meanings
(and having to deal with escaping things, etc). Making the 'policy'
things into actual options also eliminates the need for special sugar
functions all over the place (this is implemented with attribute traits
under the hood, but you shouldn't really ever need to know or care about
that).
-doy