日向夏特殊応援部隊

俺様向けメモ

Moose::Cookbook::Recipe9 - builder -

拡張可能な default と同等の機能である builder です。

ソースコード

package BinaryTree;

use Moose;

has 'node' => ( is => 'rw', isa => 'Any' );

has 'parent' => (
    is        => 'rw',
    isa       => 'BinaryTree',
    predicate => 'has_parent',
    weak_ref  => 1,
);

has 'left' => (
    is        => 'rw',
    isa       => 'BinaryTree',
    predicate => 'has_left',
    lazy      => 1,
    builder   => '_build_child_tree',
);

has 'right' => (
    is        => 'rw',
    isa       => 'BinaryTree',
    predicate => 'has_right',
    lazy      => 1,
    builder   => '_build_child_tree',
);

before 'right', 'left' => sub {
    my ( $self, $tree ) = @_;
    $tree->parent($self) if defined $tree;
};

sub _build_child_tree {
    my $self = shift;

    return BinaryTree->new( parent => $self );
}

package main;

use Perl6::Say;
use Data::Dump qw(dump);

my $tree = BinaryTree->new;

say dump $tree;

my $left = $tree->left;

say dump $tree;

解説

default を sub {} にしておき、lazy 付けたりって話は Moose::Cookbook::Recipe3 - predicate, weak_ref, lazy - - Yet Another Hackadelic で既にやりました。ソースもほとんど似たような構成です。
先にソースの実行結果を貼っておきます。

bless({}, "BinaryTree")
do {
  my $a = bless({ left => bless({ parent => 'fix' }, "BinaryTree") }, "BinaryTree");
  $a->{left}{parent} = $a;
  $a;
}

まず単純に new した段階では left, right, parent などには何も入ってません。しかし left を getter として利用した後に見てみると left に新しく BinaryTree オブジェクトが生成されているのが分かります。
default + lazy と同じ効果ですね。

default + lazy vs builder

builder は

has 'left' => (
    is        => 'rw',
    isa       => 'BinaryTree',
    predicate => 'has_left',
    lazy      => 1,
    builder   => '_build_child_tree',
);

とあるように、メソッド名を指定するだけです。で実際にメソッドが存在しますね。

sub _build_child_tree {
    my $self = shift;

    return BinaryTree->new( parent => $self );
}

実際に存在するメソッドなんだから、このメソッド上書きするなり、before, after, augment など Moose の機能をふんだんに使った拡張するなり自由に出来ますが、default の場合は直接 CODEREF を指定するので拡張性がありません。

この点が違いですね。