Categories

Posts in this category

Sun, 23 Aug 2009

Let's build an object


Permanent link

Building an object in Perl 6 is rather easy. As the author of a class you don't really have to care (at least in the simplest case), you inherit a default constructor from class Mu. As a consumer of that class you just write YourClass.new(attrib1 => $value1) to create an object of class YourClass, at the same time initializing a public attribute.

Running initializations code

If you want to run some initialization code on object creation, you don't have to touch the new method at all. Something like this works:

class C {
    submethod BUILD {
        say "Created a new instance of C";
    }
}

C.new();

The BUILD submethod is called by the constructor automatically, and can do any initialization that's necessary. It also receives the named arguments that the user passes on to new().

(In case you wonder, a submethod is a method that's not inherited to child classes).

Since BUILD is run on an object which is not yet fully constructed, attributes are only accessible if they are declared as named parameters like submethod BUILD(:$!attr1, :$!attr2) { # can use $!attr1 and $!attr2 here }. This syntax also automatically initializes the attributes with the value of the named parameter to new of the same name.

So the following class declarations behave the same:

class D {
    has $.x;
}
# and
class D {
    has $!x;                  # private attribute
    submethod BUILD(:$!x) { } # allow D.new(x => $value)
    method x() { $!x }        # accessor
}

Custom constructors

Suppose you're not a big fan of named arguments, and you want to write a constructor that takes one mandatory positional parameter. In that case you'd write a custom new method. To create an object, that method has to call self.bless:

class C {
    has $.size;
    method new($x) {
        self.bless(*, size => 2 * $x);
    }
}

say C.new(3).size;      # prints 6

The star * as the first argument to bless tells it to create an empty object itself.

If you want to enable additional named parameters, that's easily done:

class C {
    has $.size;
    method new($x, *%n) {
        self.bless(*, size => 2 * $x, |%n);
    }
}

Note that these two concepts (custom new() and BUILD() (sub)methods) are orthogonal; you can use both at once, and both peacefully coexist.

Default values of attributes

The most convenient way to provide defaults to attributes is at the point of attribute declaration:

class Window {
    has $.height = 600;
    has $.width  = $.height * 1.618;
    ...
}

The default value will only be used if the underlying attribute has not been touched by new or BUILD.

Understanding object initialization

As demonstrated above you don't need to understand the full process of building and initializing objects to manipulate it. If you still want to know, read on.

Suppose you have a class C which inherits from another class B, then the process of building an object of class C looks like this:

Perl 6 object creation

The user calls C.new (which is inherited from class Object), which in turn calls self.bless(*, |%args). bless creates a new P6Opaque object which is the storage for the newly created object. This is the call to CREATE in the image above.

After the storage has been allocated and the attributes initialized, new passes control to BUILDALL (passing along all named parameters), which in turn calls BUILD in all classes in the inheritance hierarchy, starting at the top of the hierarchy and calling the BUILD method of class C at last.

This design allows you to substitute parts of the initialization with least effort, and especially writing custom new and BUILD methods very easily.

See Also

Perl 6 Reference Documentation on object construction.

[/perl-6] Permanent link