I understand that using builder
enables subclasses to override attribute defaults easily and roles can require
them. This can also be accomplished using default
like so:
has 'foo' =>
is => 'rw',
isa => 'Str',
default => sub { $_[0]->_build_foo };
I'm wondering if there are further advantages to using builder
I'm not aware of? I've come up with some myself:
builder
is declarative so you can introspect that foo
is built by _build_foo
builder
eliminates a subroutine wrapper making it a bit fasterbuilder
allows the use of the helpful lazy_build
.UPDATE To clarify, this isn't about default
vs builder
in general but default => sub { $_[0]->_build_foo }
vs builder => '_build_foo'
.
I think you've already answered your own question. Using builder
allows late-binding, which plays nicely with roles and classes that are intended to be subclassed. It's also valuable if the builder is pretty long — I never put a default
more than a line long into an attribute definition. There's no real functional difference; default
can easily emulate builder
, but the result isn't very pretty.
Using 'builder' and 'default' appropriately can make your code easier to read and organize.
'builder' also can fit a familiar pattern of programming where private methods begin with an underscore.
has json => ( is => 'ro', default => sub { JSON->new } )
has schema => ( is => 'ro', builder => '_schema' }
sub _schema {
my $self = shift;
$self->log_debug('constructing schema') if($self->debug);
My::App::Schema->connect($self->dsn,$self->username,$self->password)
}
Additionally, using builder allows you to turn expensive functions into memoized accessors without touching the original method:
sub get_things {
my $self = shift;
return +{ map { $_ => $self->price_for($_) }
$self->wodgets->calulate_expensive_things };
Refactor with memoization:
has things => ( is => 'ro', lazy => 1, builder => 'get_things' );
Those are most of the ways I've used builder to clarify my code.
There's no difference between
default => sub { $_[0]->_build_foo }
and
builder => '_build_foo'
The primary difference between default
and builder
is that one one calls an anon sub and the other calls a named method.
has created_time_stamp => (
default => sub { time() },
);
versus
has created_time_stamp => (
builder => '_build_created_time_stamp',
);
sub _build_created_time_stamp { time() }
Using default
reduces scrolling through the code as everything is where you need it. I use it for that reason. That is uses less typing is a bonus.
It also force you to be more explicit about overriding the builder. Other answers have considered this a con, but I consider calling virtual methods on an object that hasn't even been constructed yet to be a bad practice! That's what BUILD
is for.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With