16

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 faster
  • builder 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'.

Schwern
  • 153,029
  • 25
  • 195
  • 336

3 Answers3

13

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.

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • 1
    Why do you think `default => \&builder` doesn't do every one of those things? – ikegami Apr 04 '13 at 09:26
  • 2
    @ikegami because `\&builder` will be resolved to `&builder` in the current package. A subclass that provides a new `builder` would have to do `has '+attr' => builder => \&builder` again to bind it to the overridden method. OTOH `builder => 'builder'` is resolved at runtime. – hobbs Apr 04 '13 at 13:54
  • 1
    As a counterpoint, a subclass can override the attribute and change the `default`. This does not require knowing the probably private `builder` method name. *Both* require knowing if the attribute is using `default` or `builder` because you can't have both. This is generally private information and subject to change. Overriding the default behavior of attributes in Moose is problematic, you have to publish details of how that default behavior is implemented or the subclass has to introspect. :/ – Schwern Nov 17 '15 at 20:08
4

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.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • The question is specifically about `default => sub { $_[0]->_build_foo }` vs `builder => '_build_foo'`, so subclasses can override the default, not default vs builder in general. That's why the additional sub call. – Schwern Mar 02 '12 at 10:34
  • @Schwern, Why would you do that?! No reason to do that whatsoever. Well, maybe for consistency if you've used `default` everywhere else. – ikegami Mar 02 '12 at 19:31
  • 2
    "Why would you do that" is exactly what I'm asking. It seems to me that `builder => '_build_foo'` is syntax sugar for `default => sub { $_[0]->_build_foo }`. Is there something more? – Schwern Mar 03 '12 at 02:41
4

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.

edibleEnergy
  • 1,739
  • 1
  • 13
  • 15
  • I also like to use them for attributes in say, roles, that someone might want to redefine in a class consuming said role. Seems a bit easier than redefining the whole attribute with a '+' in front of the name. – dhoss Mar 02 '12 at 07:11
  • Sorry, this isn't about `builder` vs `default` in general but `default => sub { $_[0]->_build_foo }` vs `builder => '_build_foo'`. – Schwern Mar 02 '12 at 10:36