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