Raku has a rich built-in syntax for defining and using classes.

A default constructor allows the setting of attributes for the created object:

class Point {
    has Int $.x;
    has Int $.y;
}
 
class Rectangle {
    has Point $.lower;
    has Point $.upper;
 
    method area(--> Int{
        ($!upper.x - $!lower.x* ( $!upper.y - $!lower.y);
    }
}
 
# Create a new Rectangle from two Points 
my $r = Rectangle.new(lower => Point.new(x => 0=> 0),
                      upper => Point.new(x => 10=> 10));
 
say $r.area(); # OUTPUT: «100␤» 

In the two classes, the default constructor is being used. This constructor will use named parameters in its invocation: Point.new(x => 0, y => 0).

You can also provide your own constructor and BUILD implementation. You need to do it for cases in which there are private attributes that might need to be populated during object construction, as in the example below:

# Example taken from 
# https://medium.freecodecamp.org/a-short-overview-of-object-oriented-software-design-c7aa0a622c83 
class Hero {
    has @!inventory;
    has Str $.name;
    submethod BUILD:$name:@inventory ) {
        $!name = $name;
        @!inventory = @inventory
    }
 
    method act {
        return @!inventory.pick;
    }
}
my $hero = Hero.new(:name('Þor'),
                    :inventory(['Mjölnir','Chariot','Bilskirnir']));
say $hero.act;

In this case, we encapsulate the private attribute @!inventory; but private instance variables cannot be set by the default constructor, which is why we add a BUILD submethod that takes care of that.

The following, more elaborate example, shows how a dependency handler might look in Raku. It showcases custom constructors, private and public attributes, Submethods, methods, and various aspects of signatures. It's not a lot of code, and yet the result is interesting and useful.

class Task {
    has      &!callback;
    has Task @!dependencies;
    has Bool $.done;
 
    # Normally doesn't need to be written 
    method new(&callback*@dependencies{
        return self.bless(:&callback:@dependencies);
    }
 
    # BUILD is the equivalent of a constructor in other languages 
    submethod BUILD(:&!callback:@!dependencies{ }
 
    method add-dependency(Task $dependency{
        push @!dependencies$dependency;
    }
 
    method perform() {
        unless $!done {
            .perform() for @!dependencies;
            &!callback();
            $!done = True;
        }
    }
}
 
my $eat =
    Task.new({ say 'eating dinner. NOM!' },
        Task.new({ say 'making dinner' },
            Task.new({ say 'buying food' },
                Task.new({ say 'making some money' }),
                Task.new({ say 'going to the store' })
            ),
            Task.new({ say 'cleaning kitchen' })
        )
    );
 
$eat.perform();

In this case, BUILD is needed since we have overridden the default new constructor. bless is eventually invoking it with the two named arguments that correspond to the two properties without a default value. With its signature, BUILD converts the two positionals to the two attributes, &!callback and @!dependencies, and returns the object (or turns it in to the next phase, TWEAK, if available).

Declaring new as a method and not as a multi method prevents us from using the default constructor; this implicit constructor uses the attributes as named parameters. This is one of the reasons why using new as a method's name is discouraged. If you need to declare it anyway, use multi method new if you do not want to disable the default constructor.

TWEAK is the last submethod to be called, and it has the advantage of having the object properties available without needing to use the metaobject protocol. It can be used, for instance, to assign values to instance variables based on the values of other attributes or instance variables:

class Str-with-ID is Str {
    my $counter = 0;
    has Str $.string;
    has Int $.ID;
 
    submethod TWEAK() {
        $!ID = $counter++;
    }
}
 
say Str-with-ID.new(string => 'First').ID;  # OUTPUT: «0» 
say Str-with-ID.new(string => 'Second').ID# OUTPUT: «1» 

In this case, we need to compute $.ID from the value of a counter that is a class variable, $counter, thus we simply assign a value to it and increment the counter at the same time. Please check also this section on TWEAK in the Object Orientation (OO) document for the mechanics of object construction.

DESTROY is the submethod that gets called when an object is garbage collected. That is going to happen only if the runtime needs the memory, so we can't rely on when it's going to happen. In particular, it could happen in the middle of some running code in a thread, so we must take special care to not assume any context during that event. We can use it to close any kind of handles or supplies or delete temporary files that are no longer going to be used.

my $in_destructor = 0;
 
class Foo {
    submethod DESTROY { $in_destructor++ }
}
 
my $foo;
for 1 .. 6000 {
    $foo = Foo.new();
}
 
say "DESTROY called $in_destructor times";

This might print something like DESTROY called 5701 times, but it only kicks in after we have stomped over former instances of Foo 6000 times. We can't rely, however, on the order of destruction, for instance.

Starting with class §

Raku, 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 method keyword.

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 Int and Str refer to the type object of one of the Raku 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 new method.

You can use the .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) 
 
class Foo {};
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:U{ "It's a type object!" }
multi foo (Int:D{ "It's an instance!"   }
say foo Int# OUTPUT: «It's a type object!␤» 
say foo 42;  # OUTPUT: «It's an instance!␤» 

State §

In the Task class, the first three lines inside the 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 (i.e., a bit of code to invoke in order to perform the task that an object represents):

has &!callback;

The & 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 @!dependencies;

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 is completed. 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 $.done;

This scalar attribute (with the $ sigil) has a type of Bool. Instead of the ! twigil, the . twigil is used. While Raku does enforce encapsulation on attributes, it also saves you from writing accessor methods. Replacing the ! with a . both declares a private attribute and an accessor method named after the attribute. In this case, both the attribute $!done and the accessor method done are declared. It's as if you had written:

has Bool $!done;
method done() { return $!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 returning 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 $.done is rw;

The is rw trait causes the generated accessor method to return a container so external code can modify the value of the attribute.

You can also supply default values to attributes (which works equally for those with and without accessors):

has Bool $.done = 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 @!dependencies;
has $.ready = not @!dependencies;

Writable attributes are accessible through writable containers:

class a-class {
    has $.an-attribute is rw;
}
say (a-class.new.an-attribute = "hey"); # OUTPUT: «hey␤» 

This attribute can also be accessed using the .an-attribute or .an-attribute() syntax. See also the is rw trait on classes for examples on how this works on the whole class.

A class declaration can also include class variables, which are variables whose value is shared by all instances, and can be used for things like counting the number of instantiations or any other shared state. Class variables use the same syntax as the rest of the attributes, but are declared as my or our, depending on the scope; our variables will be shared by subclasses, since they have package scope.

class Str-with-ID is Str {
    my $counter = 0;
    our $hierarchy-counter = 0;
    has Str $.string;
    has Int $.ID;
 
    method TWEAK() {
        $!ID = $counter++;
        $hierarchy-counter++;
    }
 
}
 
class Str-with-ID-and-tag is Str-with-ID {
    has Str $.tag;
}
 
say Str-with-ID.new(string => 'First').ID;  # OUTPUT: «0␤» 
say Str-with-ID.new(string => 'Second').ID# OUTPUT: «1␤» 
say Str-with-ID-and-tag.newstring => 'Third'tag => 'Ordinal' ).ID;
# OUTPUT: «2␤» 
say $Str-with-ID::hierarchy-counter;       # OUTPUT: «4␤» 

In this case, using new might be the easiest way to initialize the $.ID field and increment the value of the counter at the same time. new, through bless, will invoke the default BUILD, assigning the values to their properties correctly. You can obtain the same effect using TWEAK, which is considered a better practice by Raku experts. Please check the section on submethods for an alternative example on how to do this. Since TWEAK is called in every object instantiation, it's incremented twice when creating objects of class Str-with-ID-and-tag; this is a class hierarchy variable that is shared by all subclasses of Str-with-ID. Additionally, class variables declared with package scope are visible via their fully qualified name (FQN), while lexically scoped class variables are "private".

Static fields? §

Raku has no static keyword. Nevertheless, any class may declare anything that a module can, so making a scoped variable sounds like a good idea.

class Singleton {
    my Singleton $instance;
    method new {!!!}
    submethod instance {
        $instance = Singleton.bless unless $instance;
        $instance;
    }
}

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 HaveStaticAttr {
    my Foo $.foo = some_complicated_subroutine;
}

Class attributes may also be declared with a secondary sigil – in a similar manner to instance attributes – that will generate read-only accessors if the attribute is to be public.

Methods §

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 $dependency) {
    push @!dependencies, $dependency;
}

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 it with the . method call operator. Second, a method places its invocant into the special variable self.

The method itself takes the passed parameter – which must be an instance of the Task class – and pushes it onto the invocant's @!dependencies attribute.

The perform method contains the main logic of the dependency handler:

method perform() {
    unless $!done {
        .perform() for @!dependencies;
        &!callback();
        $!done = True;
    }
}

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.

Private methods §

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. In the following implementation of a MP3TagData class to extract ID3v1 metadata from an mp3 file, methods parse-data, can-read-format, and trim-nulls are private methods while the remaining ones are public methods:

class MP3TagData {
    has $.filename where { .IO ~~ :e };
 
    has Str $.title   is built(False);
    has Str $.artist  is built(False);
    has Str $.album   is built(False);
    has Str $.year    is built(False);
    has Str $.comment is built(False);
    has Int $.genre   is built(False);
    has Int $.track   is built(False);
    has Str $.version is built(False);
    has Str $.type    is built(False= 'ID3';
 
    submethod TWEAK {
        with $!filename.IO.open(:r:bin-> $fh {
            $fh.seek(-128SeekFromEnd);
            my $tagdata = $fh.read(128);
            self!parse-data: $tagdata;
            $fh.close;
        }
        else {
            warn "Failed to open file."
        }
    }
 
    method !parse-data($data{
        if self!can-read-format($data{
            my $offset = $data.bytes - 128;
 
            $!title  = self!trim-nulls: $data.subbuf($offset +  330);
            $!artist = self!trim-nulls: $data.subbuf($offset + 3330);
            $!album  = self!trim-nulls: $data.subbuf($offset + 6330);
            $!year   = self!trim-nulls: $data.subbuf($offset + 93,  4);
 
            my Int $track-flag = $data.subbuf($offset + 97 + 281).Int;
            $!track            = $data.subbuf($offset + 97 + 291).Int;
 
            ($!version$!comment= $track-flag == 0 && $!track != 0
                ?? ('1.1'self!trim-nulls: $data.subbuf($offset + 9728))
                !! ('1.0'self!trim-nulls: $data.subbuf($offset + 9730));
 
            $!genre = $data.subbuf($offset + 97 + 301).Int;
        }
    }
 
    method !can-read-format(Buf $data --> Bool{
        self!trim-nulls($data.subbuf(0..2)) eq 'TAG'
    }
 
    method !trim-nulls(Buf $data --> Str{
        $data.decode('utf-8').subst(/\x[0000]+/'')
    }
}

To call a private method of another class, the caller has to be trusted by the callee. 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 (FQN) of the method. A trust relationship also allows access to private attributes.

class B {...}
 
class C {
    trusts B;
    has $!hidden = 'invisible';
    method !not-yours () { say 'hidden' }
    method yours-to-use () {
        say $!hidden;
        self!not-yours();
    }
}
 
class B {
    method i-am-trusted () {
        my C $c.=new;
        $c!C::not-yours();
    }
}
 
C.new.yours-to-use(); # the context of this call is GLOBAL, and not trusted by C 
B.new.i-am-trusted();

Trust relationships are not subject to inheritance. To trust the global namespace, the pseudo package GLOBAL can be used.

Constructors §

Raku 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(&callback*@dependencies{
    return self.bless(:&callback:@dependencies);
}

The biggest difference between constructors in Raku and constructors in languages such as C# and Java is that rather than setting up state on a somehow already magically created object, Raku 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 &!callback and @!dependencies directly. To do this, we override the BUILD submethod, which is called on the brand new object by bless:

submethod BUILD(:&!callback:@!dependencies{ }

Since 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 (&!callback and @!dependencies) are being used as the bind targets for BUILD's parameters. Zero-boilerplate initialization! See objects for more information.

The BUILD method is responsible for initializing all attributes and must also handle default values:

has &!callback;
has @!dependencies;
has Bool ($.done$.ready);
submethod BUILD(
        :&!callback,
        :@!dependencies,
        :$!done = False,
        :$!ready = not @!dependencies,
    ) { }

See Object Construction for more options to influence object construction and attribute initialization.

Consuming our class §

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 $eat = Task.new({ say 'eating dinner. NOM!' });

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:

my $eat =
    Task.new({ say 'eating dinner. NOM!' },
        Task.new({ say 'making dinner' },
            Task.new({ say 'buying food' },
                Task.new({ say 'making some money' }),
                Task.new({ say 'going to the store' })
            ),
            Task.new({ say 'cleaning kitchen' })
        )
    );

Notice how the custom constructor and the sensible use of whitespace makes task dependencies clear.

Finally, the perform method call recursively calls the perform method on the various other dependencies in order, giving the output:

making some money
going to the store
buying food
cleaning kitchen
making dinner
eating dinner. NOM!

Inheritance §

Object Oriented Programming provides the concept of inheritance as one of the mechanisms for code reuse. Raku 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.

class Employee {
    has $.salary;
}
 
class Programmer is Employee {
    has @.known_languages is rw;
    has $.favorite_editor;
 
    method code_to_solve$problem ) {
        return "Solving $problem using $.favorite_editor in "
        ~ $.known_languages[0];
    }
}

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 = Programmer.new(
    salary => 100_000,
    known_languages => <Raku Perl Erlang C++>,
    favorite_editor => 'vim'
);
 
say $programmer.code_to_solve('halting problem'),
    " will get \$ {$programmer.salary()}";
# OUTPUT: «Solving halting problem using vim in Raku will get $100000␤» 

Overriding inherited methods §

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 Cook's cook method.

class Cook is Employee {
    has @.utensils  is rw;
    has @.cookbooks is rw;
 
    method cook$food ) {
        say "Cooking $food";
    }
 
    method clean_utensils {
        say "Cleaning $_" for @.utensils;
    }
}
 
class Baker is Cook {
    method cook$confection ) {
        say "Baking a tasty $confection";
    }
}
 
my $cook = Cook.new(
    utensils  => <spoon ladle knife pan>,
    cookbooks => 'The Joy of Cooking',
    salary    => 40000
);
 
$cook.cook'pizza' );       # OUTPUT: «Cooking pizza␤» 
say $cook.utensils.raku;     # OUTPUT: «["spoon", "ladle", "knife", "pan"]␤» 
say $cook.cookbooks.raku;    # OUTPUT: «["The Joy of Cooking"]␤» 
say $cook.salary;            # OUTPUT: «40000␤» 
 
my $baker = Baker.new(
    utensils  => 'self cleaning oven',
    cookbooks => "The Baker's Apprentice",
    salary    => 50000
);
 
$baker.cook('brioche');      # OUTPUT: «Baking a tasty brioche␤» 
say $baker.utensils.raku;    # OUTPUT: «["self cleaning oven"]␤» 
say $baker.cookbooks.raku;   # OUTPUT: «["The Baker's Apprentice"]␤» 
say $baker.salary;           # OUTPUT: «50000␤» 

Because the dispatcher will see the cook method on Baker before it moves up to the parent class the Baker's cook method will be called.

To access methods in the inheritance chain, use re-dispatch or the MOP.

Multiple inheritance §

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. Raku uses the C3 algorithm to linearize multiple inheritance hierarchies, which is better than depth-first search for handling multiple inheritance.

class GeekCook is Programmer is Cook {
    method new*%params ) {
        push%params<cookbooks>"Cooking for Geeks" );
        return self.bless(|%params);
    }
}
 
my $geek = GeekCook.new(
    books           => 'Learning Raku',
    utensils        => ('stainless steel pot''knife''calibrated oven'),
    favorite_editor => 'MacVim',
    known_languages => <Raku>
);
 
$geek.cook('pizza');
$geek.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.

The also declarator §

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 does.

class GeekCook {
    also is Programmer;
    also is Cook;
    # ... 
}
 
role A {};
role B {};
class C {
    also does A;
    also does B;
    # ... 
}

Introspection §

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:

my Programmer $o .= new;
if $o ~~ Employee { say "It's an employee" };
say $o ~~ GeekCook ?? "It's a geeky cook" !! "Not a geeky cook";
say $o.^name;
say $o.raku;
say $o.^methods(:local.name.join('');

The output might look like this:

It's an employee
Not a geeky cook
Programmer
Programmer.new(known_languages => ["Perl", "Python", "Pascal"],
        favorite_editor => "gvim", salary => "too small")
code_to_solve, known_languages, favorite_editor

The first two tests each smartmatch 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 GeekCook.

The call $o.^name tells us the type of $o; in this case Programmer.

$o.raku returns a string that can be executed as Raku code, and reproduces the original object $o. While this does not work perfectly in all cases, it is very useful for debugging simple objects. [1]

The syntax of calling a method with .^ instead of a single dot means that it is actually a method call on its metaclass, which is a class managing the properties of the Programmer class – or any other class you are interested in. This metaclass enables other ways of introspection too:

say $o.^attributes.join('');
say $o.^parents.map({ $_.^name }).join('');

Finally $o.^name calls the name method on the metaobject, which unsurprisingly returns the class name.

Given an object $mp3 and the MP3TagData class definition from the section Private methods, we can inquire about its public methods with .^methods:

my $mp3 = MP3TagData.new(filename => 'football-head.mp3');
say $mp3.^methods(:local);
# OUTPUT: (TWEAK filename title artist album year comment genre track version
# type Submethod+{is-hidden-from-backtrace}.new)

$mp3.^methods(:local) produces a list of Methods that can be called on $mp3. The :local named argument limits the returned methods to those defined in the MP3TagData class and excludes the inherited methods; MP3TagData inherits from no class, so providing :local makes no difference.

To check if a type object (or an instance object) implements a certain public method, use the .^find-method metamethod, which returns the method object if it exists. Otherwise, it returns Mu.

say $mp3.^find_method('name');   # OUTPUT: «(Mu)␤»
say $mp3.^find_method('artist'); # OUTPUT: «artist␤»

Type objects can also be introspected for its private methods. However, public and private methods don't use the same APIs, and thus different metamethods must be used: .^private_methods and .^find_private_method.

say $mp3.^private_methods;                     # OUTPUT: «(parse-data can-read-format trim-nulls)␤»
say $mp3.^find_private_method('parse-data');   # OUTPUT: «parse-data␤»
say $mp3.^find_private_method('remove-nulls'); # OUTPUT: «(Mu)»

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, by finding its type with .^name, seeing a construction recipe for it with .raku, 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. For instance, 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.

Overriding default gist method §

Some classes might need their own version of gist, which overrides the terse way it is printed when called to provide a default representation of the class. For instance, exceptions might want to write just the payload and not the full object so that it is clearer what to see what's happened. However, this isn't limited to exceptions; you can do that with every class:

class Cook {
    has @.utensils  is rw;
    has @.cookbooks is rw;
 
    method cook$food ) {
        return "Cooking $food";
    }
 
    method clean_utensils {
        return "Cleaning $_" for @.utensils;
    }
 
    multi method gist(Cook:U:{ '' ~ self.^name ~ '' }
    multi method gist(Cook:D:{
        '⚗ Cooks with ' ~ @.utensils.join" ‣ "~ ' using '
          ~ @.cookbooks.map"«" ~ * ~ "»").join" and "}
}
 
my $cook = Cook.new(
    utensils => <spoon ladle knife pan>,
    cookbooks => ['Cooking for geeks','The French Chef Cookbook']);
 
say Cook.gist# OUTPUT: «⚗Cook⚗» 
say $cook.gist# OUTPUT: «⚗ Cooks with spoon ‣ ladle ‣ knife ‣ pan using «Cooking for geeks» and «The French Chef Cookbook»␤» 

Usually you will want to define two methods, one for the class and another for class instances; in this case, the class method uses the alembic symbol, and the instance method, defined below it, aggregates the data we have on the cook to show it in a narrative way.

Generated from

Generated from Language/classtut.pod6