Runtime Application of Roles

Class declarations frozen at compilation time are often sufficient, but sometimes it's useful to add new behaviors to individual objects. Perl 6 allows you to do so by applying roles to individual objects at runtime.

The example in this chapter uses this to give bots new abilities during their lifetimes. The Plugins role is at the heart of this. The signature of the method on-message captures the invocant into a variable $self marked rw, which indicates that the invocant may be modified. Inside the method, that happens:

    if %pluggables{$0} -> $plug-in {
        B<$self does $plug-in;>
        return "Loaded $0";

Roles in Perl 6 are first-class entities, just like classes. You can pass roles around just like any other object. The %pluggables hash maps names of plug-ins to Role objects. The lookup inside on-message stores a Role in $plug-in. The does operator adds this role to $self--not the class of $self, but the instance itself. From this point on, $self now has all of the methods from the role, in addition to all of the ones that it had before. This does affect any other instances of the same class; only this one instance has changed.

Differences from compile time composition

Runtime application differs from compile time composition in that methods in the applied role in will automatically override any of the same name within the class of the object. It's as if you had written an anonymous subclass of the current class of the object that composed the role into it. This means that .* will find both those methods that mixed into the object from one or more roles along with any that already existed in the class.

If you wish to apply multiple roles at a time, list them all with does. This case behaves the same way as compile-time composition, in that the role composer will compose them all into the imaginary anonymous subclass. Any conflicts will occur at this point.

This gives a degree of safety, but it happens at runtime and is thus not as safe as compile time composition. For safety, perform your compositions at compile time. Instead of applying multiple roles to an instance, compose them into a new role at compile time and apply that role to the instance.

The but operator

Runtime role application with does modifies an object in place: $x does SomeRole modifies the object stored in $x. Sometimes this modification is not what you want. In that case, use the but operator, which clones the object, performs the role composition with the clone, and returns the clone. The original object stays the same.

TODO: example