Object Orientation

Object Orientation in Perl 6

Perl 6 provides strong support for object oriented programming. Although Perl 6 allows programmers to program in multiple paradigms, object oriented programming is at the heart of the language.

Perl 6 comes with a wealth of predefined types, which can be classified in two categories: normal and native types.

Native types are used for low-level types (like uint64). They do not have the same capabilities as objects, though if you call methods on them, they are boxed into normal objects.

Everything that you can store in a variable is either a native value or an object. That includes literals, types (type objects), code and containers.

Using Objects

To call a method on an object, add a dot, followed by the method name:

say "abc".uc;
# OUTPUT: «ABC␤»

This calls the uc method on "abc", which is an object of type Str. To supply arguments to the method, add arguments inside parentheses after the method.

my $formatted-text = "Fourscore and seven years ago...".indent(8);

$formatted-text now contains the above text, but indented 8 spaces.

Multiple arguments are separated by commas:

my @words = "Abe", "Lincoln";
@words.push("said", $formatted-text.comb(/\w+/));

Multiple arguments can be specified by separating the argument list with a colon:

say @words.join: '--';
# OUTPUT: «Abe--Lincoln--said--Fourscore--and--seven--years--ago␤»

Since you have to put a : after the method if you want to pass arguments without parentheses, a method call without a colon or parentheses is unambiguously a method call without an argument list:

say 4.log:   ; # OUTPUT: «1.38629436111989␤» ( natural logarithm of 4 ) 
say 4.log: +2# OUTPUT: «2␤» ( base-2 logarithm of 4 ) 
say 4.log  +2# OUTPUT: «3.38629436111989␤» ( natural logarithm of 4, plus 2 ) 

Many operations that don't look like method calls (for example, smart matching, or interpolating an object into a string) result in method calls under the hood.

Methods can return mutable containers, in which case you can assign to the return value of a method call. This is how read-writable attributes to objects are used:

$*IN.nl-in = "\r\n";

Here, we call method nl-in on the $*IN object, without arguments, and assign to the container it returned with the = operator.

All objects support methods from class Mu, which is the type hierarchy root. All objects derive from Mu.

Type Objects

Types themselves are objects and you can get the type object by writing its name:

my $int-type-obj = Int;

You can ask any object for its type object by calling the WHAT method (which is actually a macro in method form):

my $int-type-obj = 1.WHAT;

Type objects (other than Mu) can be compared for equality with the === identity operator:

sub f(Int $x) {
    if $x.WHAT === Int {
        say 'you passed an Int';
    }
    else {
        say 'you passed a subtype of Int';
    }
}

Although, in most cases, the .isa method will suffice:

sub f($x) {
    if $x.isa(Int) {
        ...
    }
    ...
}

Subtype checking is done by smart-matching:

if $type ~~ Real {
    say '$type contains Real or a subtype thereof';
}

Classes

Classes are declared using the class keyword, typically followed by a name.

class Journey {
}

This declaration results in a type object being created and installed in the current package and current lexical scope under the name Journey. You can also declare classes lexically:

my class Journey {
}

This restricts their visibility to the current lexical scope, which can be useful if the class is an implementation detail nested inside a module or another class.

Attributes

Attributes are variables that exist per instance of a class. They are where the state of an object is stored. In Perl 6, all attributes are private. They are typically declared using the has declarator and the ! twigil.

class Journey {
    has $!origin;
    has $!destination;
    has @!travelers;
    has $!notes;
}

While there is no such thing as a public (or even protected) attribute, there is a way to have accessor methods generated automatically: replace the ! twigil with the . twigil (the . should remind you of a method call).

class Journey {
    has $.origin;
    has $.destination;
    has @!travelers;
    has $.notes;
}

This defaults to providing a read-only accessor. In order to allow changes to the attribute, add the is rw trait:

class Journey {
    has $.origin;
    has $.destination;
    has @!travelers;
    has $.notes is rw;
}

Now, after a Journey object is created, its .origin, .destination, and .notes will all be accessible from outside the class, but only .notes can be modified.

If an object is instantiated without certain attributes, such as origin or destination, we may not get the desired result . To prevent this, provide default values or make sure that an attribute is set on object creation by marking an attribute with an is required trait.

class Journey {
    # error if origin is not provided 
    has $.origin is required;
    # set the destination to Orlando as default (unless that is the origin!) 
    has $.destination = self.origin eq 'Orlando' ?? 'Kampala' !! 'Orlando';
    has @!travelers;
    has $.notes is rw;
}

Since classes inherit a default constructor from Mu and we have requested that some accessor methods are generated for us, our class is already somewhat functional.

# Create a new instance of the class.
my $vacation = Journey.new(
    origin      => 'Sweden',
    destination => 'Switzerland',
    notes       => 'Pack hiking gear!'
);

# Use an accessor; this outputs Sweden.
say $vacation.origin;

# Use an rw accessor to change the value.
$vacation.notes = 'Pack hiking gear and sunglasses!';

Note that the default constructor will only set attributes that have an accessor method, but it can initialize read-only attributes.

Methods

Methods are declared with the method keyword inside a class body.

class Journey {
    has $.origin;
    has $.destination;
    has @!travelers;
    has $.notes is rw;

    method add_traveler($name) {
        if $name ne any(@!travelers) {
            push @!travelers, $name;
        }
        else {
            warn "$name is already going on the journey!";
        }
    }

    method describe() {
        "From $!origin to $!destination"
    }
}

A method can have a signature, just like a subroutine. Attributes can be used in methods and can always be used with the ! twigil, even if they are declared with the . twigil. This is because the . twigil declares a ! twigil and generates an accessor method.

Looking at the code above, there is a subtle but important difference between using $!origin and $.origin in the method describe. The first is always a lookup of the attribute. The first is an inexpensive and obvious lookup of the attribute. $.origin is a method call and thus may be overridden in a subclass. Only use $.origin if you want to allow overriding.

Unlike Subroutines, additional named arguments will not produce compile time or runtime errors. That allows chaining of methods via Re-dispatching

Method names can be resolved at runtime with the ."" operator.

class A { has $.b };
my $name = 'b';
A.new."$name"().say;
# OUTPUT: «(Any)␤» 

self

Inside a method, the term self is available, which is bound to the invocant (the object the method was called on). self can be used to call further methods on the invocant. Within methods, $.origin works the same as self.origin, however the colon-syntax for method arguments is only supported for method calls using self, not the shortcut.

Note that if the relevant methods bless, CREATE, BUILDALL of Mu are not overloaded, self will point to the type object in those methods.

Since user defined constructors are called on type objects, self will point to the type object in constructors and the submethod BUILD. Initializers are also called before object construction time. This can be used to access class attributes in initializers.

class C {
    my $.local = 42;
    has $.x = self.local
};
say C.new.x
# OUTPUT: «42␤» 

Private Methods

Methods with an exclamation mark ! before the method name are not callable from anywhere outside the defining class; such methods are private in the sense that they are not visible from outside the class that declares them. Private methods are invoked with an exclamation mark instead of a dot:

method !do-something-private($x{
    ...
}
method public($x{
    if self.precondition {
        self!do-something-private(2 * $x)
    }
}

Private methods are not inherited by subclasses.

Submethods

Submethods are public methods that are not inherited by subclasses. The name stems from the fact that they are semantically similar to subroutines.

Submethods are useful for object construction and destruction tasks, as well as for tasks that are so specific to a certain type that subtypes must certainly override them.

For example, the default method new calls submethod BUILD on each class in an inheritance chain:

class Point2D {
    has $.x;
    has $.y;
 
    submethod BUILD(:$!x:$!y{
        say "Initializing Point2D";
    }
}
 
class InvertiblePoint2D is Point2D {
    submethod BUILD() {
        say "Initializing InvertiblePoint2D";
    }
    method invert {
        self.new(x => - $.x=> - $.y);
    }
}
 
say InvertiblePoint2D.new(x => 1=> 2);

This produces the following output:

Initializing Point2D
Initializing InvertiblePoint2D
InvertiblePoint2D.new(x => 1=> 2)

See also: Object Construction.

Inheritance

Classes can have parent classes.

class Child is Parent1 is Parent2 { }

If a method is called on the child class, and the child class does not provide that method, the method of that name in one of the parent classes is invoked instead, if it exists. The order in which parent classes are consulted is called the method resolution order (MRO). Perl 6 uses the C3 method resolution order. You can ask a type for its MRO through a call to its meta class:

say List.^mro;      # List() Cool() Any() Mu()

If a class does not specify a parent class, Any is assumed by default. All classes directly or indirectly derive from Mu, the root of the type hierarchy.

All calls to public methods are "virtual" in the C++ sense, which means that the actual type of an object determines which method to call, not the declared type:

class Parent {
    method frob {
        say "the parent class frobs"
    }
}
 
class Child is Parent {
    method frob {
        say "the child's somewhat more fancy frob is called"
    }
}
 
my Parent $test;
$test = Child.new;
$test.frob;          # calls the frob method of Child rather than Parent 

This produces the output:

the child's somewhat more fancy frob is called

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 = 2 * $!x;
}
my $p = Point.new( x => 5, y => 2);
#             ^^^ inherited from class Mu
say "x: ", $p.x;
say "y: ", $p.y;

This outputs:

x: 5
y: 2
my $p2 = Point.new( x => 5 );
# the given value for x is used to calculate the right
# value for y.
say "x: ", $p.x;
say "y: ", $p.y;

This outputs:

x: 5
y: 10

Mu.new 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 (the 2 * $!x in the example above).

TWEAK 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.new( encoding => 'UTF-8', data => [64, 65] );
my $b2 = EncodedBuffer.new( enc      => 'UTF-8', data => [64, 65] );
#  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) {
        self.bless(:$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 creation 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;

Object Cloning

The Mu parent class, from which all classes inherit, supplies a method named clone, which is somewhat magical in that it can copy values from an object's private attributes to create a new object. This cloning is shallow since it only binds attributes to the same values contained in the original object; it does not make copies of those values.

As with new, public attributes can be set to initial values. These override values received from the original object. (See the documentation for Mu's clone for an example.)

Note that since clone is not a submethod, a class which provides its own clone method will replace the Mu method. There is no automatic mechanism like BUILDALL for cloning. For example, if one wished to make clone deeper for a particular class, one would will probably want to use callwith or nextwith to push the deeper copies to superclasses:

class A {
    has $.a;
    #... 
    method clone {
        nextwith(:a($.a.clone))
    }
}

This works well for simple classes, but in some cases one might need to follow BUILDALL's lead and work in reverse method resolution order:

class B is A {
    has $.b;
    #... 
    method clone {
        my $obj = callsame;
        $obj.b = $!b.clone(:seed($obj.a.generate_seed));
        $obj
    }
}

Roles

Roles are in some ways similar to classes, in that they are a collection of attributes and methods. They differ in that roles are also meant for describing only parts of an object's behavior and in how roles are applied to classes. Or to phrase it differently, classes are meant for managing objects and roles are meant for managing behavior and code reuse.

use MONKEY-SEE-NO-EVAL;
role Serializable {
    method serialize() {
        self.perl; # very primitive serialization
    }
    method deserialize($buf) {
        EVAL $buf; # reverse operation to .perl
    }
}

class Point does Serializable {
    has $.x;
    has $.y;
}
my $p = Point.new(:x(1), :y(2));
my $serialized = $p.serialize;      # method provided by the role
my $clone-of-p = Point.deserialize($serialized);
say $clone-of-p.x;      # OUTPUT: «1␤»

Roles are immutable as soon as the compiler parses the closing curly brace of the role declaration.

Role Application

Role application differs significantly from class inheritance. When a role is applied to a class, the methods of that role are copied into the class. If multiple roles are applied to the same class, conflicts (e.g. attributes or non-multi methods of the same name) cause a compile-time error, which can be solved by providing a method of the same name in the class.

This is much safer than multiple inheritance, where conflicts are never detected by the compiler, but are instead resolved to the superclass that appears earlier in the method resolution order, which might not be what the programmer wanted.

For example, if you've discovered an efficient method to ride cows, and are trying to market it as a new form of popular transportation, you might have a class Bull, for all the bulls you keep around the house, and a class Automobile, for things that you can drive.

class Bull {
    has Bool $.castrated = False;
    method steer {
        # Turn your bull into a steer 
        $!castrated = True;
        return self;
    }
}
class Automobile {
    has $.direction;
    method steer($!direction{ }
}
class Taurus is Bull is Automobile { }
 
my $t = Taurus.new;
$t.steer# OUTPUT: «Castrates $t␤» 

With this setup, your poor customers will find themselves unable to turn their Taurus and you won't be able to make more of your product! In this case, it may have been better to use roles:

role Bull-Like {
    has Bool $.castrated = False;
    method steer {
        # Turn your bull into a steer 
        $!castrated = True;
        return self;
    }
}
role Steerable {
    has Real $.direction;
    method steer(Real $d = 0{
        $!direction += $d;
    }
}
class Taurus does Bull-Like does Steerable { }

This code will die with something like:

===SORRY!===
Method 'steer' must be resolved by class Taurus because it exists in
multiple roles (SteerableBull-Like)

This check will save you a lot of headaches:

class Taurus does Bull-Like does Steerable {
    method steer($direction?{
        self.Steerable::steer($direction?)
    }
}

When a role is applied to a second role, the actual application is delayed until the second role is applied to a class, at which point both roles are applied to the class. Thus

role R1 {
    # methods here 
}
role R2 does R1 {
    # methods here 
}
class C does R2 { }

produces the same class C as

role R1 {
    # methods here 
}
role R2 {
    # methods here 
}
class C does R1 does R2 { }

Stubs

When a role contains a stubbed method, a non-stubbed version of a method of the same name must be supplied at the time the role is applied to a class. This allows you to create roles that act as abstract interfaces.

role AbstractSerializable {
    method serialize() { ... }  # literal ... here marks the
                                      # method as a stub
}

# the following is a compile time error, for example
#        Method 'serialize' must be implemented by Point because
#        it's required by a role
class APoint does AbstractSerializable {
    has $.x;
    has $.y;
}

# this works:
class SPoint does AbstractSerializable {
    has $.x;
    has $.y;
    method serialize() { "p($.x, $.y)" }
}

The implementation of the stubbed method may also be provided by another role.

Inheritance

Roles cannot inherit from classes, but they may cause any class which does that role to inherit from another class. So if you write:

role A is Exception { }
class X::Ouch does A { }
X::Ouch.^parents.say # OUTPUT: «((Exception))␤» 

...then X::Ouch will inherit directly from Exception, as we can see above by listing its parents.

Pecking order

A method defined directly in a class will always override definitions from applied roles or from inherited classes. If no such definition exists, methods from roles override methods inherited from classes. This happens both when said class was brought in by a role, and also when said class was inherited directly.

Note that each candidate for a multi-method is its own method... in this case, the above only applies if two such candidates have the same signature. Otherwise, there is no conflict, and the candidate is just added to the multi-method.

Automatic Role Punning

Any attempt to directly instantiate a role, as well as many other operations on it, will automatically create an instance of a class with the same name as the role, making it possible to transparently use a role as if it were a class.

role Point {
    has $.x;
    has $.y;
    method abs { sqrt($.x * $.x + $.y * $.y}
}
say Point.new(x => 6=> 8).abs;

We call this automatic creation of classes punning, and the generated class a pun.

Parameterized Roles

Roles can be parameterized, by giving them a signature in square brackets:

role BinaryTree[::Type{
    has BinaryTree[Type$.left;
    has BinaryTree[Type$.right;
    has Type $.node;
 
    method visit-preorder(&cb{
        cb $.node;
        for $.left$.right -> $branch {
            $branch.visit-preorder(&cbif defined $branch;
        }
    }
    method visit-postorder(&cb{
        for $.left$.right -> $branch {
            $branch.visit-postorder(&cbif defined $branch;
        }
        cb $.node;
    }
    method new-from-list(::?CLASS:U: *@el{
        my $middle-index = @el.elems div 2;
        my @left         = @el[0 .. $middle-index - 1];
        my $middle       = @el[$middle-index];
        my @right        = @el[$middle-index + 1 .. *];
        self.new(
            node    => $middle,
            left    => @left  ?? self.new-from-list(@left)  !! self,
            right   => @right ?? self.new-from-list(@right!! self,
        );
    }
}
 
my $t = BinaryTree[Int].new-from-list(456);
$t.visit-preorder(&say);    # OUTPUT: «5␤4␤6␤» 
$t.visit-postorder(&say);   # OUTPUT: «4␤6␤5␤» 

Here the signature consists only of a type capture, but any signature will do:

use v6.c;
 
enum Severity <debug info warn error critical>;
 
role Logging[$filehandle = $*ERR{
    method log(Severity $sev$message{
        $filehandle.print("[{uc $sev}$message\n");
    }
}
 
Logging[$*OUT].log(debug'here we go');        # OUTPUT: «[DEBUG] here we go␤» 

You can have multiple roles of the same name, but with different signatures; the normal rules of multi dispatch apply for choosing multi candidates.

Mixins of Roles

Roles can be mixed into objects. A role's given attributes and methods will be added to the methods and attributes the object already has. Multiple mixins and anonymous roles are supported.

role R { method Str() {'hidden!'} };
my $i = 2 but R;
sub f(\bound){ put bound };
f($i); # OUTPUT: «hidden!␤» 

Note that the object got the role mixed in, not the object's class or the container. Thus, @-sigiled containers will require binding to make the role stick. Some operators will return a new value, which effectively strips the mixin from the result.

Mixins can be used at any point in your object's life.

# A counter for Table of Contents 
role TOC-Counter {
    has Int @!counters is default(0);
    method Str() { @!counters.join: '.' }
    method inc($level{
        @!counters[$level - 1]++;
        @!counters.splice($level);
        self
    }
}
 
my Num $toc-counter = NaN;     # don't do maths with Not A Number 
say $toc-counter;              # OUTPUT: «NaN␤» 
$toc-counter does TOC-Counter# now we mix the role in 
$toc-counter.inc(1).inc(2).inc(2).inc(1).inc(2).inc(2).inc(3).inc(3);
put $toc-counter / 1;          # OUTPUT: «NaN␤» (because that's numerical context) 
put $toc-counter;              # OUTPUT: «2.2.2␤» (put will call TOC-Counter::Str) 

Roles can be anonymous.

my %seen of Int is default(0 but role :: { method Str() {'NULL'} });
say %seen<not-there>;          # OUTPUT: «NULL␤» 
say %seen<not-there>.defined;  # OUTPUT: «True␤» (0 may be False but is well defined) 
say Int.new(%seen<not-there>); # OUTPUT: «0␤» 

Meta-Object Programming and Introspection

Perl 6 has a meta object system, which means that the behavior of objects, classes, roles, grammars, enums, etc. are themselves controlled by other objects; those objects are called meta objects. Meta objects are, like ordinary objects, instances of classes, in this case we call them meta classes.

For each object or class you can get the meta object by calling .HOW on it. Note that although this looks like a method call, it works more like a macro.

So, what can you do with the meta object? For one you can check if two objects have the same meta class by comparing them for equality:

say 1.HOW ===   2.HOW;      # OUTPUT: «True␤» 
say 1.HOW === Int.HOW;      # OUTPUT: «True␤» 
say 1.HOW === Num.HOW;      # OUTPUT: «False␤» 

Perl 6 uses the word HOW, Higher Order Workings, to refer to the meta object system. Thus it should be no surprise that in Rakudo, the class name of the meta class that controls class behavior is called Perl6::Metamodel::ClassHOW. For each class there is one instance of Perl6::Metamodel::ClassHOW.

But of course the meta model does much more for you. For example, it allows you to introspect objects and classes. The calling convention for methods on meta objects is to call the method on the meta object and pass in the object of interest as first argument to the object. So to get the name of the class of an object, you could write:

my $object = 1;
my $metaobject = 1.HOW;
say $metaobject.name($object);      # OUTPUT: «Int␤» 
 
# or shorter: 
say 1.HOW.name(1);                  # OUTPUT: «Int␤» 

(The motivation is that Perl 6 also wants to allow a more prototype-based object system, where it's not necessary to create a new meta object for every type).

There's a shortcut to keep from using the same object twice:

say 1.^name;                        # OUTPUT: «Int␤» 
# same as 
say 1.HOW.name(1);                  # OUTPUT: «Int␤» 

See Metamodel::ClassHOW for documentation on the meta class of class and also the general documentation on the meta object protocol.