syntax class

Documentation for syntax class assembled from the following types:

language documentation Object orientation

From Object orientation

(Object orientation) declarator class class

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; when instantiated to a value, the association between the variable and its value is called a property. They are where the state of an object is stored. In Raku, all attributes are private, which means they can be accessed directly only by the class instance itself. 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, although the default constructor can initialize read-only attributes, it will only set attributes that have an accessor method. That is, even if you pass travelers => ["Alex", "Betty"] to the default constructor, the attribute @!travelers is not initialized.

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. $!origin 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.

You may write your own accessors to override any or all of the autogenerated ones.

my $ = " " xx 4# A tab-like thing 
class Journey {
    has $.origin;
    has $.destination;
    has @.travelers;
    has Str $.notes is rw;
 
    multi method notes() { "$!notes\n" };
    multi method notesStr $note ) { $!notes ~= "$note\n$" };
 
    method Str { "⤷ $!origin\n$" ~ self.notes() ~ "$!destination ⤶\n" };
}
 
my $trip = Journey.new:origin<Here>:destination<There>,
                        travelers => <þor Freya> );
 
$trip.notes("First steps");
notes $trip: "Almost there";
print $trip;
 
# OUTPUT: 
#⤷ Here 
#       First steps 
#       Almost there 
# 
#There ⤶ 

The declared multi method notes overrides the auto-generated methods implicit in the declaration of $.notes, using a different signature for reading and writing.

Please note that in notes $trip: "Almost there" we are using indirect invocant syntax, which puts first the method name, then the object, and then, separated by a colon, the arguments: method invocant: arguments. We can use this syntax whenever it feels more natural than the classical period-and-parentheses one. It works exactly in the same way.

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

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

The syntax used to update $.notes changed in this section with respect to the previous Attributes section. Instead of an assignment:

$vacation.notes = 'Pack hiking gear and sunglasses!';

we now do a method call:

$trip.notes("First steps");

Overriding the default auto-generated accessor means it is no longer available to provide a mutable container on return for an assignment. A method call is the preferred approach to adding computation and logic to the update of an attribute. Many modern languages can update an attribute by overloading assignment with a “setter” method. While Raku can overload the assignment operator for this purpose with a Proxy object, overloading assignment to set attributes with complex logic is currently discouraged as weaker object oriented design.

Class and instance methods

A method's signature can have an explicit invocant as its first parameter followed by a colon, which allows for the method to refer to the object it was called on.

class Foo {
    method greet($me: $person{
        say "Hi, I am $me.^name(), nice to meet you, $person";
    }
}
Foo.new.greet("Bob");    # OUTPUT: «Hi, I am Foo, nice to meet you, Bob␤» 

Providing an invocant in the method signature also allows for defining the method as either as a class method, or as an object method, through the use of type constraints. The ::?CLASS variable can be used to provide the class name at compile time, combined with either :U (for class methods) or :D (for instance methods).

class Pizza {
    has $!radius = 42;
    has @.ingredients;
 
    # class method: construct from a list of ingredients 
    method from-ingredients(::?CLASS:U $pizza: @ingredients{
        $pizza.newingredients => @ingredients );
    }
 
    # instance method 
    method get-radius(::?CLASS:D:{ $!radius }
}
my $p = Pizza.from-ingredients: <cheese pepperoni vegetables>;
say $p.ingredients;     # OUTPUT: «[cheese pepperoni vegetables]␤» 
say $p.get-radius;      # OUTPUT: «42␤» 
say Pizza.get-radius;   # This will fail. 
CATCH { default { put .^name ~ ":\n" ~ .Str } };
# OUTPUT: «X::Parameter::InvalidConcreteness:␤ 
#          Invocant of method 'get-radius' must be 
#          an object instance of type 'Pizza', 
#          not a type object of type 'Pizza'. 
#          Did you forget a '.new'?» 

A method can be both a class and object method by using the multi declarator:

class C {
    multi method f(::?CLASS:U:{ say "class method"  }
    multi method f(::?CLASS:D:{ say "object method" }
}
C.f;       # OUTPUT: «class method␤» 
C.new.f;   # OUTPUT: «object method␤» 

self

Inside a method, the term self is available and bound to the invocant object. self can be used to call further methods on the invocant, including constructors:

class Box {
  has $.data;
 
  method make-new-box-from() {
      self.new: data => $!data;
  }
}

self can be used in class or instance methods as well, though beware of trying to invoke one type of method from the other:

class C {
    method g()            { 42     }
    method f(::?CLASS:U:{ self.g }
    method d(::?CLASS:D:{ self.f }
}
C.f;        # OUTPUT: «42␤» 
C.new.d;    # This will fail. 
CATCH { default { put .^name ~ ":\n" ~ .Str } };
# OUTPUT: «X::Parameter::InvalidConcreteness:␤ 
#          Invocant of method 'f' must be a type object of type 'C', 
#          not an object instance of type 'C'.  Did you forget a 'multi'?» 

self can also be used with attributes, as long as they have an accessor. self.a will call the accessor for an attribute declared as has $.a. However, there is a difference between self.a and $.a, since the latter will itemize; $.a will be equivalent to self.a.item or $(self.a).

class A {
    has Int @.numbers;
    has $.x = (123);
 
    method show-diff() { .say for self.x.say for $.x }
 
    method twice  { self.times: 2 }
    method thrice { $.times: 3    }
 
    method times($val = 1{ @!numbers.map(* * $val).list }
};
 
my $obj = A.new(numbers => [123]);
$obj.show-diff;   # OUTPUT: «1␤2␤3␤(1 2 3)␤» 
say $obj.twice;   # OUTPUT: «(2 4 6)␤» 
say $obj.thrice;  # OUTPUT: «(3 6 9)␤» 

The colon-syntax for method arguments is supported for method calls using either self or the shortcut, as illustrated with the methods twice and thrice in the example above.

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

On the other hand, the submethods BUILD and TWEAK are called on instances, in different stages of initialization. Submethods of the same name from subclasses have not yet run, so you should not rely on potentially virtual method calls inside these methods.

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:

class FunMath {
    has $.value is required;
    method !do-subtraction$num ) {
        if $num ~~ Str {
            return $!value + (-1 * $num.chars);
        }
        return $!value + (-1 * $num);
    }
    method minus$minuend: $subtrahend ) {
        # invoking the private method on the explicit invocant 
        $minuend!do-subtraction($subtrahend);
    }
}
my $five = FunMath.new(value => 5);
say $five.minus(6);         # OUTPUT: «-1␤» 
 
say $five.do-subtraction(6);
CATCH { default { put .^name ~ ":\n" ~ .Str } }
# OUTPUT: «X::Method::NotFound: 
# No such method 'do-subtraction' for invocant of type 
# 'FunMath'. Did you mean '!do-subtraction'?␤» 

Private methods are not inherited by subclasses.

Submethods

Submethods are public methods that will not be 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 would certainly have to 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);
# OUTPUT: «Initializing Point2D␤» 
# OUTPUT: «Initializing InvertiblePoint2D␤» 
# OUTPUT: «InvertiblePoint2D.new(x => 1, y => 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). Raku uses the C3 method resolution order. You can ask a type for its MRO through a call to its metaclass:

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 
# 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;
}
my $p = Point.newx => 5=> 2);
#             ^^^ inherited from class Mu 
say "x: "$p.x;
say "y: "$p.y;
# OUTPUT: «x: 5␤» 
# OUTPUT: «y: 2␤» 

Mu.new calls method bless on its invocant, passing all the named arguments. bless creates the new object, and then 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. This means that BUILD may change an attribute, but it does not have access to the contents of the attribute declared as its default; these are available only during TWEAK (see below), which can 'see' the contents of an attribute initialized in the declaration of the class.

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. See an example of its use below.

Due to the default behavior of BUILD and TWEAK 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 or TWEAK 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{
        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 Raku. It is merely a common convention, one that is followed quite thoroughly in most Raku classes. You can call bless from any method at all, or use CREATE to fiddle around with low-level workings.

The TWEAK submethod 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␤» 

Object cloning

The cloning is done using the clone method available on all objects, which shallow-clones both public and private attributes. New values for public attributes can be supplied as named arguments.

class Foo {
    has $.foo = 42;
    has $.bar = 100;
}
 
my $o1 = Foo.new;
my $o2 = $o1.clone: :bar(5000);
say $o1# Foo.new(foo => 42, bar => 100) 
say $o2# Foo.new(foo => 42, bar => 5000) 

See document for clone for details on how non-scalar attributes get cloned, as well as examples of implementing your own custom clone methods.