buildall (method) Object Construction

Documentation for buildall (method) Object Construction, assembled from the following types:

declarator class

From class

(class) buildall (method) Object Construction

Objects are generally created through method calls, either on the type object or on another object of the same type.

Class Mu provides a constructor method called new, which takes named arguments and uses them to initialize public attributes.

class Point {
    has $.x;
    has $.y;
my $p = Point.newx => 5=> 2);
#             ^^^ inherited from class Mu 
say "x: "$p.x;
say "y: "$p.y;
# OUTPUT: «x: 5␤» 
# OUTPUT: «y: 2␤» calls method bless on its invocant, passing all the named arguments. bless creates the new object and then calls method BUILDALL on it. BUILDALL walks all subclasses in reverse method resolution order (i.e. from Mu to most derived classes) and in each class checks for the existence of a method named BUILD. If the method exists, the method is called with all the named arguments from the new method. If not, the public attributes from this class are initialized from named arguments of the same name. In either case, if neither BUILD nor the default mechanism has initialized the attribute, default values are applied.

After the BUILD methods have been called, methods named TWEAK are called, if they exist, again with all the named arguments that were passed to new.

Due to the default behavior of BUILDALL and BUILD submethods, named arguments to the constructor new derived from Mu can correspond directly to public attributes of any of the classes in the method resolution order, or to any named parameter of any BUILD submethod.

This object construction scheme has several implications for customized constructors. First, custom BUILD methods should always be submethods, otherwise they break attribute initialization in subclasses. Second, BUILD submethods can be used to run custom code at object construction time. They can also be used for creating aliases for attribute initialization:

class EncodedBuffer {
    has $.enc;
    has $.data;
    submethod BUILD(:encoding(:$enc), :$data{
        $!enc  :=  $enc;
        $!data := $data;
my $b1 = EncodedBuffer.newencoding => 'UTF-8'data => [6465] );
my $b2 = EncodedBuffer.newenc      => 'UTF-8'data => [6465] );
#  both enc and encoding are allowed now 

Since passing arguments to a routine binds the arguments to the parameters, a separate binding step is unnecessary if the attribute is used as a parameter. Hence the example above could also have been written as:

submethod BUILD(:encoding(:$!enc), :$!data{
    # nothing to do here anymore, the signature binding 
    # does all the work for us. 

However, be careful when using this auto-binding of attributes when the attribute may have special type requirements, such as an :$!id that must be a positive integer. Remember, default values will be assigned unless you specifically take care of this attribute, and that default value will be Any, which would cause a type error.

The third implication is that if you want a constructor that accepts positional arguments, you must write your own new method:

class Point {
    has $.x;
    has $.y;
    method new($x$y{

However this is considered poor practice, because it makes correct initialization of objects from subclasses harder.

Another thing to note is that the name new is not special in Perl 6. It is merely a common convention. You can call bless from any method at all, or use CREATE to fiddle around with low-level workings.

Another pattern of hooking into object construction is by writing your own method BUILDALL. To make sure that initialization of superclasses works fine, you need to callsame to invoke the parent classes BUILDALL.

class MyClass {
    method BUILDALL(|) {
        # initial things here 
        callsame;   # call the parent classes (or default) BUILDALL 
        # you can do final checks here. 
        self # return the fully built object 

The TWEAK method allows you to check things or modify attributes after object construction:

class RectangleWithCachedArea {
    has ($.x1$.x2$.y1$.y2);
    has $.area;
    submethod TWEAK() {
        $!area = abs( ($!x2 - $!x1* ( $!y2 - $!y1) );
say RectangleWithCachedArea.newx2 => 5x1 => 1y2 => 1y1 => 0).area;
# OUTPUT: «4␤»