📘 Class attributes in Perl 6

Class data variables are called attributes. They are declared with the has keyword. An attribute’s scope is defined via its twigil. As usual, the first character of the twigil indicates the type of the container (thus, a scalar, an array, or a hash). The second character is either . if a variable is public or ! for the private ones. An accessor will be generated by a compiler for the public attributes.

class Cafe {
    has $.name;
    has @!orders;
}

To create or instantiate an object of the class X, the constructor is called: X.new(). This is basically a method derived from the Any class (this is one of the classes on the top of the object system in Perl 6).

my $cafe = Cafe.new(
    name => "Paris"
);

At this point, you can read public attributes.

say $cafe.name;

Reading from $.name is possible because, by default, all public fields are readable and a corresponding access method for them is created. However, that does not allow changing the attribute. To make a field writable, indicate it explicitly by adding the is rw trait.

class Cafe {
    has $.name is rw;
    has @!orders;
}

my $cafe = Cafe.new(
    name => "Paris"
);

Now, read and write actions are available.

$cafe.name = "Berlin";
say $cafe.name;

📘 Submethods in Perl 6 classes

Perl 6 defines the so-called submethods for classes. These are the methods which are not propagating to the subclass’s definition. The submethods may be either private or public, but they will not be inherited by the children.

class A {
    submethod submeth {
        say "A.submeth"
    }
}

class B is A {
}

my A $a;
my B $b;

$a.submeth;   # OK
# $b.submeth; # Error: No such method 'submeth' 
              # for invocant of type 'B'

Multiple inheritance in Perl 6

When more than one class is mentioned in the list of base classes, we have multiple inheritance.

class A {
    method a {
        say "A.a"
    }
}

class B {
    method b {
        say "B.b";
    }
}

class C is A is B {
}

my $c = C.new;
$c.a;
$c.b;

With multiple inheritance, method resolution order is more important, as different base classes may have methods with the same name, or, for example, the two base classes have another common parent. This is why you should know the order of the base class now.

class A {
    method meth {
        say "A.meth"
    }
}

class B {
    method meth {
        say "B.meth";
    }
}

class C is A is B {
}

class D is B is A {
}

Here, the method named meth exists in both parent classes A and B, thus calling it on variables of the types C and D will be resolved differently.

my $c = C.new;
$c.meth; # A.meth

my $d = D.new;
$d.meth; # B.meth

This behaviour is confirmed by the method resolution order list, which is actually used by the compiler.

$c.^mro.say; # ((C) (A) (B) (Any) (Mu))
$d.^mro.say; # ((D) (B) (A) (Any) (Mu))

📘 Chapter 4. Classes

We have already seen elements of the object-oriented programming in Perl 6. Methods may be called on those variables, which do not look like real objects from the first view. Even more, methods may be called on constants.

The types that were used earlier (like Int or Str) are container types. Variables of a container type can contain values corresponding to some native representation. The compiler does all the conversion it needs for executing a programme. For example, when it sees 42.say, it calls the say method, which the Int object inherits from the top of the type hierarchy in Perl 6.

Perl 6 also supports object-oriented programming in its general understanding. If you are familiar with how to use classes in other modern programming languages, it will be easy for you to work with classes in Perl 6.

This is how the class is declared:

class Cafe {
}

📘 Class methods in Perl 6

The method keyword defines a method, similarly to how we define subroutines with sub. A method has access to all attributes of the class, both public and private.

The method itself can be private. We will return to this later after talking about inheritance.

In the following short example, two methods are created, and each of them manipulates the private @!orders array.

class Cafe {
    has $.name;
    has @!orders;

    method order($what) {
        @!orders.push($what);
    }

    method list-orders {
        @!orders.sort.join(', ').say;
    }
}

my $cafe = Cafe.new(
    name => "Paris"
);

$cafe.order('meat');
$cafe.order('fish');
$cafe.list-orders; # fish, meat

The code should be quite readable for people familiar with OOP. Just keep in mind that “everything is an object” and you may chain method calls.

@!orders.sort.join(', ').say;

Instance methods receive a special variable, self (having no sigil), which points to the current object. It can be used to access instance data or the class methods.

method order($what) {
    @!orders.push($what);
    self.list-orders;
}

method list-orders {
    say self.name;
    @!orders.sort.join(', ').say;
}

📘 Inheritance in Perl 6

Inheritance is easy. Just say is Baseclass when declaring a class. Having said that, your class will be derived from the base class.

class A {
    method x {
        say "A.x"
    }
    method y {
        say "A.y"
    }
}

class B is A {
    method x {
        say "B.x"
    }
}

The further usage of the inherited classes is straightforward.

my $a = A.new;
$a.x; # A.x
$a.y; # A.y

my $b = B.new;
$b.x; # B.x
$b.y; # A.y

It is important that the result of the method search does not depend on which type was used to declare a variable. Perl 6 always will first use the methods belonging to the class of the variable, which is currently stored in the variable container. For example, return to the previous example and declare the variable $b to be one of type A, but still create an instance of B with B.new. Even in that case, calling $b.x will still lead to the method defined in the derived class.

my A $b = B.new;
$b.x; # B.x
$b.y; # A.y

Meta-methods (which also are available for every object without writing any code) provide information about the class details. In particular, to see the exact order in which method resolution will be executed, call the .^mro metamethod.

say $b.^mro;

In our example, the following order will be printed.

((B) (A) (Any) (Mu))

Of course, you may call the .^mro method on any other variable or object in a programme, regardless of whether it is an instance of the user-defined class, a simple variable, or a constant. Just get an idea of how this is implemented internally.

$ perl6 -e'42.^mro.say'
((Int) (Cool) (Any) (Mu))

📘 Private (closed) methods in Perl 6 classes

Now, after we have discussed inheritance, let us return to the private (or closed) methods. These methods may only be used within the class itself. Thus, you cannot call them from the programme that uses an instance of the class. Nor are they accessible in the derived classes. An exclamation mark is used to denote a private method.

The following example demonstrates the usage of a private method of a class. The comments in the code will help you to understand how it works.

class A {
    # Method is only available within A
    method !private {
        say "A.private";
    }

    # Public method calling a private method
    method public {
        # You cannot avoid self here.
        # Consider the '!' as a separator like '.'
        self!private;
    }
}

class B is A {
    method method {
        # Again self, but this the '.' this time.
        # This is a public method.
        self.public;

        # This will be a compile-time error.
        # self!private;
    }
}

my $b = B.new;
$b.method; # A.private

The exclamation mark is actually part of the method name. So you can have both method meth and method !meth in the same class. To access them, use self.meth and self!meth, respectively:

class C {
    method meth  {say 'meth' }
    method !meth {say '!meth'}
    method demo {
        self.meth;
        self!meth;
    }
}

my $c = C.new;
$c.demo; # Prints both meth and !meth