Classes and Objects
A tutorial for creating and using classes in Perl 6
Perl 6 has a rich built-in syntax for defining and using classes.
A default constructor allows the setting of attributes for the created object:
# Create a new Rectangle from two Pointsmy = Rectangle.new(lower => Point.new(x => 0, y => 0), upper => Point.new(x => 10, y => 10));say .area(); # OUTPUT: «100␤»
You can also provide your own construction and build implementation. The following, more elaborate example shows how a dependency handler might look in Perl 6. It showcases custom constructors, private and public attributes, methods and various aspects of signatures. It's not very much code, and yet the result is interesting and useful.
Perl 6, like many other languages, uses the
class keyword to define a class. The block that follows may contain arbitrary code, just as with any other block, but classes commonly contain state and behavior declarations. The example code includes attributes (state), introduced through the
has keyword, and behaviors, introduced through the
Declaring a class creates a new type object which, by default, is installed into the current package (just like a variable declared with
our scope). This type object is an "empty instance" of the class. For example, types such as
Str refer to the type object of one of the Perl 6 built-in classes. The example above uses the class name
Task so that other code can refer to it later, such as to create class instances by calling the
You can use
.DEFINITE method to find out if what you have is an instance or a type object:
say Int.DEFINITE; # OUTPUT: «False␤» (type object)say 426.DEFINITE; # OUTPUT: «True␤» (instance);say Foo.DEFINITE; # OUTPUT: «False␤» (type object)say Foo.new.DEFINITE; # OUTPUT: «True␤» (instance)
You can also use type smileys to only accept instances or type objects:
multi foo (Int)multi foo (Int)say foo Int; # OUTPUT: «It's a type object!␤»say foo 42; # OUTPUT: «It's an instance!␤»
The first three lines inside the class block all declare attributes (called fields or instance storage in other languages). Just as a
my variable cannot be accessed from outside its declared scope, attributes are not accessible outside of the class. This encapsulation is one of the key principles of object oriented design.
The first declaration specifies instance storage for a callback – a bit of code to invoke in order to perform the task that an object represents:
& sigil indicates that this attribute represents something invocable. The
! character is a twigil, or secondary sigil. A twigil forms part of the name of the variable. In this case, the
! twigil emphasizes that this attribute is private to the class.
The second declaration also uses the private twigil:
has Task ;
However, this attribute represents an array of items, so it requires the
@ sigil. These items each specify a task that must be completed before the present one can complete. Furthermore, the type declaration on this attribute indicates that the array may only hold instances of the
Task class (or some subclass of it).
The third attribute represents the state of completion of a task:
has Bool ;
This scalar attribute (with the
$ sigil) has a type of
Bool. Instead of the
! twigil, the
. twigil is used. While Perl 6 does enforce encapsulation on attributes, it also saves you from writing accessor methods. Replacing the
! with a
. both declares the attribute
$!done and an accessor method named
done. It's as if you had written:
has Bool ;method done()
Note that this is not like declaring a public attribute, as some languages allow; you really get both a private attribute and a method, without having to write the method by hand. You are free instead to write your own accessor method, if at some future point you need to do something more complex than return the value.
Note that using the
. twigil has created a method that will provide read-only access to the attribute. If instead the users of this object should be able to reset a task's completion state (perhaps to perform it again), you can change the attribute declaration:
has Bool is rw;
is rw trait causes the generated accessor method to return something external code can modify to change the value of the attribute.
You can also supply default values to attributes (which works equally for those with and without accessors):
has Bool = False;
The assignment is carried out at object build time. The right-hand side is evaluated at that time, and can even reference earlier attributes:
has Task ;has = not ;
Perl 6 has no static keyword. Nevertheless, any class may declare anything that a module can, so making a scoped variable sounds like good idea.
Class attributes defined by my or our may also be initialized when being declared, however we are implementing the Singleton pattern here and the object must be created during its first use. It is not 100% possible to predict the moment when attribute initialization will be executed, because it can take place during compilation, runtime or both, especially when importing the class using the use keyword.
Class attributes may also be declared with a secondary sigil – in a similar manner to object attributes – that will generate read-only accessors if the attribute is to be public.
While attributes give objects state, methods give objects behaviors. Let's ignore the
new method temporarily; it's a special type of method. Consider the second method,
add-dependency, which adds a new task to a task's dependency list.
method add-dependency(Task )
In many ways, this looks a lot like a
sub declaration. However, there are two important differences. First, declaring this routine as a method adds it to the list of methods for the current class. Thus any instance of the
Task class can call this method with the
. method call operator. Second, a method places its invocant into the special variable
The method itself takes the passed parameter – which must be an instance of the
Task class – and
pushes it onto the invocant's
perform method contains the main logic of the dependency handler:
It takes no parameters, working instead with the object's attributes. First, it checks if the task has already completed by checking the
$!done attribute. If so, there's nothing to do.
Otherwise, the method performs all of the task's dependencies, using the
for construct to iterate over all of the items in the
@!dependencies attribute. This iteration places each item – each a
Task object – into the topic variable,
$_. Using the
. method call operator without specifying an explicit invocant uses the current topic as the invocant. Thus the iteration construct calls the
.perform() method on every
Task object in the
@!dependencies attribute of the current invocant.
After all of the dependencies have completed, it's time to perform the current
Task's task by invoking the
&!callback attribute directly; this is the purpose of the parentheses. Finally, the method sets the
$!done attribute to
True, so that subsequent invocations of
perform on this object (if this
Task is a dependency of another
Task, for example) will not repeat the task.
Just like attributes, methods can also be private. Private methods are declared with a prefixed exclamation mark. They are called with
self! followed by the method's name. To call a private method of another class the calling class has to be trusted by the called class. A trust relationship is declared with
trusts and the class to be trusted must already be declared. Calling a private method of another class requires an instance of that class and the fully qualified name of the method. Trust also allows access to private attributes.
C.new.yours-to-use(); # the context of this call is GLOBAL, and not trusted by CB.new.i-am-trusted();
Trust relationships are not subject to inheritance. To trust the global namespace, the pseudo package
GLOBAL can be used.
Perl 6 is rather more liberal than many languages in the area of constructors. A constructor is anything that returns an instance of the class. Furthermore, constructors are ordinary methods. You inherit a default constructor named
new from the base class
Mu, but you are free to override
new, as this example does:
method new(, *)
The biggest difference between constructors in Perl 6 and constructors in languages such as C# and Java is that rather than setting up state on a somehow already magically created object, Perl 6 constructors create the object themselves. The easiest way to do this is by calling the bless method, also inherited from Mu. The
bless method expects a set of named parameters to provide the initial values for each attribute.
The example's constructor turns positional arguments into named arguments, so that the class can provide a nice constructor for its users. The first parameter is the callback (the thing which will execute the task). The rest of the parameters are dependent
Task instances. The constructor captures these into the
@dependencies slurpy array and passes them as named parameters to
bless (note that
:&callback uses the name of the variable – minus the sigil – as the name of the parameter).
Private attributes really are private. This means that
bless is not allowed to bind things to
@!dependencies directly. To do this, we override the
BUILD submethod, which is called on the brand new object by
submethod BUILD(:, :)
BUILD runs in the context of the newly created
Task object, it is allowed to manipulate those private attributes. The trick here is that the private attributes (
@!dependencies) are being used as the bind targets for
BUILD's parameters. Zero-boilerplate initialization! See objects for more information.
BUILD method is responsible for initializing all attributes and must also handle default values:
has ;has ;has Bool (, );submethod BUILD(:,:,: = False,: = not)
See Object Construction for more options to influence object construction and attribute initialization.
After creating a class, you can create instances of the class. Declaring a custom constructor provides a simple way of declaring tasks along with their dependencies. To create a single task with no dependencies, write:
my = Task.new();
An earlier section explained that declaring the class
Task installed a type object in the namespace. This type object is a kind of "empty instance" of the class, specifically an instance without any state. You can call methods on that instance, as long as they do not try to access any state;
new is an example, as it creates a new object rather than modifying or accessing an existing object.
Unfortunately, dinner never magically happens. It has dependent tasks:
Notice how the custom constructor and sensible use of whitespace makes task dependencies clear.
perform method call recursively calls the
perform method on the various other dependencies in order, giving the output:
making some moneygoing to the storebuying foodcleaning kitchenmaking dinnereating dinner. NOM!
Object Oriented Programming provides the concept of inheritance as one of the mechanisms for code reuse. Perl 6 supports the ability for one class to inherit from one or more classes. When a class inherits from another class it informs the method dispatcher to follow the inheritance chain to look for a method to dispatch. This happens both for standard methods defined via the method keyword and for methods generated through other means, such as attribute accessors.
Now, any object of type Programmer can make use of the methods and accessors defined in the Employee class as though they were from the Programmer class.
my = Programmer.new(salary => 100_000,known_languages => <Perl5 Perl6 Erlang C++>,favorite_editor => 'vim');.code_to_solve('halting problem');.pay();
Of course, classes can override methods and attributes defined by parent classes by defining their own. The example below demonstrates the
Baker class overriding the
is Employeeis Cookmy = Cook.new(utensils => <spoon ladle knife pan>,cookbooks => 'The Joy of Cooking',salary => 40000);.cook( 'pizza' ); # OUTPUT: «Cooking pizza␤»say .utensils.perl; # OUTPUT: «["spoon", "ladle", "knife", "pan"]␤»say .cookbooks.perl; # OUTPUT: «["The Joy of Cooking"]␤»say .salary; # OUTPUT: «40000␤»my = Baker.new(utensils => 'self cleaning oven',cookbooks => "The Baker's Apprentice",salary => 50000);.cook('brioche'); # OUTPUT: «Baking a tasty brioche␤»say .utensils.perl; # OUTPUT: «["self cleaning oven"]␤»say .cookbooks.perl; # OUTPUT: «["The Baker's Apprentice"]␤»say .salary; # OUTPUT: «50000␤»
Because the dispatcher will see the
cook method on
Baker before it moves up to the parent class the
cook method will be called.
As mentioned before, a class can inherit from multiple classes. When a class inherits from multiple classes the dispatcher knows to look at both classes when looking up a method to search for. Perl 6 uses the C3 algorithm to linearize multiple inheritance hierarchies, which is better than depth-first search for handling multiple inheritance.
is Programmer is Cookmy = GeekCook.new(books => 'Learning Perl 6',utensils => ('stainless steel pot', 'knife', 'calibrated oven'),favorite_editor => 'MacVim',known_languages => <Perl6>);.cook('pizza');.code_to_solve('P =? NP');
Now all the methods made available to the Programmer and the Cook classes are available from the GeekCook class.
While multiple inheritance is a useful concept to know and occasionally use, it is important to understand that there are more useful OOP concepts. When reaching for multiple inheritance it is good practice to consider whether the design wouldn't be better realized by using roles, which are generally safer because they force the class author to explicitly resolve conflicting method names. For more information on roles see Roles.
Classes to be inherited from can be listed in the class declaration body by prefixing the
is trait with
also. This also works for the role composition trait
Introspection is the process of gathering information about some objects in your program, not by reading the source code, but by querying the object (or a controlling object) for some properties, such as its type.
Given an object
$o and the class definitions from the previous sections, we can ask it a few questions:
if ~~ Employee ;if ~~ GeekCook ;say .WHAT;say .perl;say .^methods(:local)».name.join(', ');say .^name;
The output can look like this:
It's an employee(Programmer)Programmer.new(known_languages => ["Perl", "Python", "Pascal"],favorite_editor => "gvim", salary => "too small")code_to_solve, known_languages, favorite_editorProgrammer
The first two tests each smart-match against a class name. If the object is of that class, or of an inheriting class, it returns true. So the object in question is of class
Employee or one that inherits from it, but not
.WHAT method returns the type object associated with the object
$o, which tells us the exact type of
$o: in this case
$o.perl returns a string that can be executed as Perl code, and reproduces the original object
$o. While this does not work perfectly in all cases, it is very useful for debugging simple objects. 
$o.^methods(:local) produces a list of Methods that can be called on
:local named argument limits the returned methods to those defined in the
Programmer class and excludes the inherited methods.
The syntax of calling a method with
.^ instead of a single dot means that it is actually a method call on its meta class, which is a class managing the properties of the
Programmer class – or any other class you are interested in. This meta class enables other ways of introspection too:
say .^attributes.join(', ');say .^parents.map().join(', ');
$o.^name calls the
name method on the meta object, which unsurprisingly returns the class name.
Introspection is very useful for debugging and for learning the language and new libraries. When a function or method returns an object you don't know about, finding its type with
.WHAT, seeing a construction recipe for it with
.perl, and so on, you'll get a good idea of what its return value is. With
.^methods you can learn what you can do with the class.
But there are other applications too: a routine that serializes objects to a bunch of bytes needs to know the attributes of that object, which it can find out via introspection.