Si potrebbe giocare con l'inizializzazione pigra:
package Node;
use Moose;
has parent => (
is => 'ro',
isa => 'Node',
lazy => 1,
init_arg => undef,
builder => '_build_parent',
);
has _parent => (
is => 'ro',
init_arg => 'parent',
);
has child => (
is => 'ro',
isa => 'Node',
lazy => 1,
init_arg => undef,
builder => '_build_child',
);
has _child => (
is => 'ro',
init_arg => 'child',
predicate => undef,
);
has name => is => 'ro', isa => 'Str';
generare i costruttori e predicati al volo:
BEGIN {
for (qw/ parent child /) {
no strict 'refs';
my $squirreled = "_" . $_;
*{"_build" . $squirreled} = sub {
my($self) = @_;
my $proto = $self->$squirreled;
ref $proto eq "REF" ? $$proto : $proto;
};
*{"has" . $squirreled} = sub {
my($self) = @_;
defined $self->$squirreled;
};
}
}
Questo permette
my $a = Node->new(parent => \my $b, name => "A");
$b = Node->new(child => $a, name => "B");
for ($a, $b) {
print $_->name, ":\n";
if ($_->has_parent) {
print " - parent: ", $_->parent->name, "\n";
}
elsif ($_->has_child) {
print " - child: ", $_->child->name, "\n";
}
}
La sua uscita è
A:
- parent: B
B:
- child: A
Il codice potrebbe essere più elegante, con η-conversion, ma Moose non passerà i parametri ai metodi costruttore.
Forse una domanda stupida, ma perché importa l'immutabilità? – Ether
Per me è più facile ragionare sugli oggetti 'Node'. Per altri motivi vedi il tag 'immutability'. – zoul