Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moose "builder" vs "default"

Tags:

perl

moose

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'.

like image 699
Schwern Avatar asked Mar 02 '12 00:03

Schwern


3 Answers

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.

like image 79
hobbs Avatar answered Nov 08 '22 01:11

hobbs


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.

like image 33
edibleEnergy Avatar answered Nov 08 '22 00:11

edibleEnergy


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.

like image 4
ikegami Avatar answered Nov 08 '22 01:11

ikegami