Compile Time Composition

Look at the KarmaKeeper class declaration. The body is empty; the class defines no attributes or methods of its own. The class inherits from IRCBot, using the is trait modifier--something familiar from earlier chapters--but it also uses the does trait modifier to compose two roles into the class.

The process of role composition is simple. Perl takes the attributes and methods defined in each role and copies them into the class. After composition, the class appears as if those attributes and methods had been declared in the class's declaration itself. This is part of the flattening property: after composing a role into the class, the roles in and of themselves are only important when querying the class to determine if it performs the role. Querying the methods of the KarmaKeeper class through introspection will report that the class has both a process method and an on-message multi method.

If this were all that roles provided, they'd have few advantages over inheritance or mixins. Roles get much more interesting in the case of a conflict. Consider the class definition:

    class MyBot is IRCBot does AnswerToAll does AnswerIfTalkedTo {}

Both the AnswerToAll and AnswerIfTalkedTo roles provide a method named process. Even though they share a name, the methods perform semantically different--and conflicting--behaviors. The role composer will produce a compile-time error about this conflict, asking the programmer to provide a resolution.

Multiple inheritance and mixin mechanisms rarely provide this degree of conflict resolution. In those situations, the order of inheritance or mixin decides which method wins. All possible roles are equal in role composition.

What can you do if there is a conflict? In this case, it makes little sense to compose both of the roles into a class. The programmer here has made a mistake and should choose to compose only one role to provide the desired behavior. An alternative way to resolve a conflict is to write a method with the same name in the class body itself:

    class MyBot is IRCBot does AnswerToAll does AnswerIfTalkedTo {
        method process($raw-in) {
            # Do something sensible here...
        }
    }

If the role composer detects a method with the same name in the class body, it will then disregard all of the (possibly conflicting) ones from the roles. Put simply, methods in the class always supersede methods which a role may provide.

Multi-methods and composition

Sometimes it's okay to have multiple methods of the same name, provided they have different signatures such that the multidispatch mechanism can distinguish between them. Multi methods with the same name from different roles will not conflict with each other. Instead, the candidates from all of the roles will combine during role composition.

If the class provides a method of the same name that is also multi, then all methods defined in the role and the class will combine into a set of multi candidates. Otherwise, if the class has a method of the same name that is not declared as a multi, then the method in the class alone--as usual--will take precedence. This is the mechanism by which the AdminBot class can perform the appropriate on-message method provided by both the KarmaTracking and the Oping roles.

When a class composes multiple roles, an alternate declaration syntax may be more readable:

    class KarmaKeeper is IRCBot {
        does AnswerToAll;
        does KarmaTracking;
        does Oping;
    }

Calling all candidates

The process methods of the roles AnswerToAll and AnswerIfTalkedTo use a modified syntax for calling methods:

    self.*on-message($msg<sender>, $msg<message>)

The .* method calling syntax changes the semantics of the dispatch. Just as the * quantifier in regexes means "zero or more", the .* dispatch operator will call zero or more matching methods. If no on-message multi candidates match, the call will not produce an error. If more than one on-message multi candidate matches, Perl will call all of them, whether found by multiple dispatch, searching the inheritance hierarchy, or both.

There are two other variants. .+ greedily calls all methods but dies unless it can call at least one method. .?, tries to call one method, but returns a Failure rather then throwing an exception. These dispatch forms may seem rare, but they're very useful for event driven programming. One-or-failure is very useful when dealing with per-object role application.

Expressing requirements

The role AnswerIfTalkedTo declares a stub for the method bot-nick, but never provides an implementation.

    method bot-nick() { ... }

In the context of a role, this means that any class which composes this role must somehow provide a method named bot-nick. The class itself may provide it, another role must provide it, or a parent class must provide it. IRCBot does the latter; it IRCBot defines an attribute $!bot-nick along with an accessor method.

If you do not make explicit the methods on which your role depends, the role composer will not verify their existence at compilation time. Any missing methods will cause runtime errors (barring the use of something like AUTOMETH). As compile-time verification is an important feature of roles, it's best to mark your dependencies.