Perl 6: new, BUILD, BUILDALL…oh my!
First note: similarly to Perl 5, the
new
method has no special meaning in Perl 6! Let me get straight about this:
Mu
(the root class) provides a
new
method, but here is no special meaning about it and it is possible to call a constructor the way you like.
What really matters are the following methods:
bless
,
BUILD
and
BUILDALL
, even if the latter two are
submethods (i.e, special methods
attached to the inheritance chain).
The idea is as follows:
1) when
new
is invoked on a class object, the method produces a new instance of the same object the class defines, and this is performed via
bless
(do you remember Perl 5?);
2) the method
new
then invokes
BUILDALL
with all named parameters (essentially, an hash);
3)
BUILDALL
traverses the inheritance chain from the top to the bottom, and on each sub-instance invokes
BUILD
(of course, if does exist);
4)
BUILD
performs object initialization, usually the binding of values.
The
submethod BUILD
is the intended hook for customizable object construction.
The
bless
method is, in fact, the default constructor itself:
method bless(*%attrinit) {
nqp::create(self).BUILDALL(Empty, %attrinit);
}
bless
does create the instance and invokes
BUILDALL
on it, triggering the initialization across the inheritance chain. However,
new
does pretty much the same invoking
bless
or performing the very same action:
multi method new(*%attrinit) {
nqp::if(
nqp::eqaddr(
(my $bless := nqp::findmethod(self,'bless')),
nqp::findmethod(Mu,'bless')
),
nqp::create(self).BUILDALL(Empty, %attrinit),
nqp::invokewithcapture($bless,nqp::usecapture)
)
}
It is possible to construct your own
new
constructor implementation based on
bless
, for instance:
class Person {
has $.name;
submethod BUILD( :$name ){
say 'Person.BUILD invoked with ' ~ $name;
$!name = $name;
}
method new( $person_name, $person_surname ){
say 'Person.new invoked with ' ~ $person_name ~ ' and ' ~ $person_surname;
my $name = $person_name ~ ' ' ~ $person_surname;
self.bless( :$name );
}
}
and if the positional
new
is invoked:
my $p = Person.new( 'Luca', 'Ferrari' );
it does result in the initialization structure being triggered:
Person.new invoked with Luca and Ferrari
Person.BUILD invoked with Luca Ferrari
In the case of a subclass, with a custom
new
, the inheritance chain is walked the right way from top to the bottom, but the positional constructor of the parent class is not used:
class Child is Person {
has $.game;
has $.age;
submethod BUILD( :$name, :$game, :$age ){
say 'Child.BUILD invoked with ' ~ $name ~ ' and ' ~ $game ~ ' and ' ~ $age;
$!game = $game;
$!age = $age;
}
method new( $name, $game, $age ){
self.bless( :$name, :$game, :$age );
}
}
and in fact, if the new instance is created with
my $c = Child.new( 'Diego', 'archery', 10 );
the result is:
Person.BUILD invoked with Diego
Child.BUILD invoked with Diego and archery and 10
Please note that when dealing with custom
new
the initialization by subclasses can be wrong, because the initialization itself performs two different paths (explicit
new
call,
bless
call).
For more discussion, please see
this issue and my comments.