A significant new part of Perl 6’s object model and type system is the addition of roles. Part of their origin is an implementation in Smalltalk (there called traits). They also solve some systemic problems of other OO systems.
Why are they useful and how do they work?
What is a Role?
A role is a named collection of behavior — a set of methods identified by a unique name. This resembles a class or a type, in that referring to the role refers to the combined set of behaviors, but it is more general than a class and more concrete than a type.
Put another way, a role is an assertion about a set of capabilities. For
example, a Doglike role identifies the important behavior that
any doglike entity will possess: perhaps a tail attribute and
the methods wag() and bark().
Even only this much is a great advantage to using roles. Presuming you
have a method that needs something Doglike, you can ask “Does
the class or object I receive behave in a Doglike fashion?”
and take the appropriate action.
Bad Approaches to The Problem
You might already have encountered this problem. A common pattern is to
create an abstract Dog object and requiring that any doglike
object inherit from that Dog object. This allows you to check
that the object or class you receive inherits from
Dog.
This “solves” the problem in a bad way in all but very simple systems.
Problems with Inheritance
In a single-inheritance system, where a class can inherit from one and only one ancestor, the abstract base class strategy takes away your option of inheriting from any other ancestor, even if another would make more sense.
In a multiple-inheritance system, if you need to mark a particular class
as Dog-like you now have the potential for weird method
dispatch resolution errors. Can you guarantee that you’ll only ever get the
right methods at the right times, or that no one will ever add another
method to an ancestor class and override the right method? Suppose
Dog itself extends Mammal and someone adds a
method to that with the same name as a method in a class appearing
after Dog in your class’s list of ancestors. Changes
are fragile and the effects can appear far, far away.
Even further, there’s a design smell with this approach: it
forces a particular class design strategy upon other classes. You
know that you have the behavior of a Dog because of
inheritance, but there are many other ways to get Dog-like
behavior. You can reimplement the methods yourself. You can use a
delegation or proxying relationship to contain one or many Dog
objects and wrap all accesses to their methods. You can use a
mock-Dog pattern for testing.
Requiring that all doglike entities actually specifically
inherit from Dog–when all your code really cares about is
that the objects or classes behave in a Doglike way–worries
too much about how the classes and objects behave, not
what they do. Inheritance is one way to ensure that a
class or object implements a known interface appropriately, but the
important part of polymorphism is that you don’t know how an
implementation works, only that it does the right thing.
Forcing the use of inheritance outside of your classes is like expecting
every iterator to maintain an internal stack of items to return or every
webserver to be Apache httpd, rather than being able to call
next_element() or speak HTTP.
(Perl 5 allows you to overload isa() to get past
poorly-designed systems that violate the encapsulation of classes and
objects they merely use, but there are plenty of error patterns around that
method too.)
Problems with Duck Typing
Other systems wisely eschew checking the structure and implementation of objects and classes–specifically, how they provide an interface–but substitute checks for the presence of specifically-named methods. This is duck typing (or very loose ).
Duck typing argues that anything that can bark() is
obviously a Dog. That works sometimes, but imagine that your
system includes, besides the Dog class, a Tree
class. Tree objects have the bark attribute
available through the bark() accessor.
You can call the method bark() on a Tree, just
as you can on a Dog, but the two methods, however similar in
name, have completely different semantics. They’re false cognates. Objects
of either class are not substitutable. Duck typing ignores this.
In practice, this may not often be a problem. Duck typing advocates
argue, correctly, that effective testing strategies catch such errors in
the rare cases when they occur. However, giving up the association between
class name and method name is a loss of expressivity. There are already
perfectly-serviceable namespaces for both the Dog’s
bark() and the Tree’s bark(). Why
should a language lose that information if it’s already in the system? Why
should an implementation ignore that information, if it’s easy to use?
Problems with Java Interfaces
The designers of Java apparently identified this problem, but invented new problems when trying to solve it.
Java interfaces are abstract, named collections of method signatures (and little else). They allow you to identify a collection of behavior with a name and request that a class or object implement that behavior.
The problems come mostly from the poor design and implementation.
Classes and interfaces occupy separate namespaces in Java, to some degree. The
syntax for querying that an object or class extends another class is
different from the syntax for querying that an object or class implements
an interface.
This approach also requires that you know too much about how an object or class performs specific behaviors.
This is most painful when dealing with code you cannot change. If the code explicitly requires that a passed-in object or class inherit from another class–that is, the function signature specifically names a class or error-checking code at the start of the function explicitly checks that the argument is or inherits from a specific class, you cannot pass in a class or object that uses another object strategy. You’re stuck. Likewise, if the immutable code requires that your class or object implement an interface, that’s your only option. You’re slightly better off that way, but it’s not perfect either — and it’s rarely the default case, as it takes much more work to define an interface and use it than to define a class.
If the library requires a specific class, not an interface, and someone
has declared that class final, you’re in real trouble. This is
perhaps the worst possible case with all of Java’s language features. This
anti-social behavior is evident even in parts of the Java standard
library.
Another sin of Java’s interfaces is that they disallow code reuse. When the option is between allowing object implementation strategies other than inheritance but duplicating code and forcing the use of inheritance in related code but not duplicating code, people tend to take the somewhat-obvious latter choice.
Then again, Java has always been verbose, and the current state of the art for Java programming appears to be to use ever-more-powerful IDEs to generate boilerplate code, so one argument is to use better tools to work around a language’s design deficiencies.
What Roles Actually Do
What actually is a role?
It’s a named set of capabilities, with optional default behavior.
What is a class? It’s an instantiable set of one or more roles.
What is a type? It’s a role. It’s a named collection of behavior.
What’s a program? It’s a set of instructions for manipulating data in terms of roles — collections of supported behavior.
A role is an interface, in the behavior sense. It’s a guarantee that
anything for which the question “Are you Doglike?” is true
will behave in a Dog-like way.
More concretely, it’s the difference between saying “This method only
ever takes String objects and prints them to a log file” and
saying “This method only ever takes Stringifiable things,
stringifies them, and prints them to a log file.” The former cares
how it works. The latter cares that it works.
Perl makes this easier with automatic coercion when you use an object in a string context, but you have to remember to overload the stringification operator. Languages without automatic coercion make this much more difficult.
Roles can also provide behavior. This is an option, not a requirement. A
Serializable role can provide default implementations that
know how to inspect hash-based objects in Perl 5, while allowing for other
objects or roles to perform the Serializable role for
array-based objects, for example. To the outside world, it only matters
that objects of both roles are Serializable, not that one uses
a hash and one uses an array.
This is the second great philosophical realization about roles: not all useful behavior fits into inheritance hierarchies. Code reuse is more important than inheritance, so there should be ways to reuse code in situations where inheritance is not useful or helpful or necessary. As well, decomposing collections of behavior into roles can help to improve design in such a way that a class can compose only the roles it needs directly, rather than accidentally inheriting several unrelated methods.
Role Implementation Notes
There are two important parts of working with roles. The first is marking a class or object as performing a role. The second is querying that a class or object performs a role.
One possibility for marking a class or object as performing a role is allowing the compiler to detect this automatically, based on duck typing likelihoods. This suffers from the same false cognate problem as normal duck typing; it’s unlikely that static analysis could identify all of the potential roles in a system as well as identify good names for them. (Names are for humans, not computers.)
A likelier solution is to require specific declaration of roles and annotation of classes and objects that perform those roles. This is an implementation detail of the class, so it’s okay to use different syntax for “this class extends this ancestor” and “this class implements a role”.
Maintaining the distinction for the querying side may help people design
better code by being more thoughtful about roles. In addition, there
may be cases where explicit queries about inheritance
relationships may be useful (perhaps in reflection or editor-support
analyses). Using a separate meta-method, such as does(), seems
effective.
As for the actual implementation, marking a class as performing a role adds the behavior of that role to the class immediately, at compile-time. For each method the role provides, that method becomes an available method on the class with the role’s implementation, unless the class already provides that method.
Obviously a class can provide its own implementations for every method of a role and still implement that role — the same goes for delegation or aggregation relationships. The role resolver merely has to know, when it runs, that the class unambiguously performs the behavior of the role.
Roles may have dependencies as well. Resolving them happens at compile
time. A role may require that the class implementing that role also
implement other roles. A Sortable role may require the
implementation of a Comparable role, for example, to be sure
that any existing compare() method does the right thing.
Checking only for the existence of a compare() method
may suffer from the false cognate problem.
Of course, the ambiguous bark() problem needs a solution as
well. The role resolver must detect conflicts in role method names and
require disambiguation. When you try to define a DogwoodTree
that does both Doglike and Treelike, you must
disambiguate explicitly, whether composing in one method or the other or
providing your own bark() method that redispatches to the
appropriate parent depending on context.
Advanced Roles
For optimal benefit in the system, any type checking (such as in method signatures) should query for roles first, then inheritance. A type system that cares more about capabilities than structural identity removes an entire world of pain–specifically the case when you need to work with a library you cannot modify that did not explicitly disallow the use of roles.
Now you have to use more syntax and do more work to prevent people from using roles.
Classes Imply Roles
If the does() question falls back to the isa()
question (that is, if a type check first queries that the entity performs a
role and then that it or its class inherits from an ancestor), there’s an
implicit relationship implied between roles and classes.
In other words, every class explicitly defined in the system should implicitly declare a role of the same name. More likely, classes and roles occupy the same namespace. A class is a specialization of a role that is instantiable. Both classes and roles provide names for and define collections of behavior.
This also implies that it should be possible to declare that one class
performs the role of another class. A CustomerProxy class may
explicitly say that it does Customer. Regardless of the object
strategy the code uses, the type system should consider that it behaves as
a Customer and is usable any place that requests a
Customer without modifying that code.
In a language with class-implies-role and a role-based type system, this requires almost no syntax:
class SomeClass
{
method some_method { ... }
}
class AnotherClass does SomeClass
{
method some_method { ... }
}
The unmodifiable library problem moves further away.
Runtime Application
In a robust object system with a capable meta-model (basically a formalized system for customizing the behavior of classes and objects), it’s even possible to apply a role to an object at runtime.
Why would you do this? The obvious answers are decorating an object with logging or debugging code, but consider all of the uses of the Decorator pattern. I recently built a small game using this pattern. Each enemy element had two parts, a shape and color and movement behavior. The visual portion used a specific role and the movement portion used another role. The game used the Factory pattern to create enemies and applied a random visual and movement role to each. Thus I only had to create an abstract role for each role type and several concrete roles for each type of movement or visuals. Adding the roles to the objects allowed for several combinations of behavior — n times m combinations, where I only had to write n plus m roles.
In systems that do not allow runtime generation of new classes, it’s still possible to get some of the benefit of runtime role application with code generation and more use of the Factory pattern.
Parameterization
As long as a role provides the appropriate methods at role
composition time, does it really matter how the role provides
those methods? In a sufficiently dynamic language, it could generate them
based on the phase of the moon (probably not useful apart from a
Lycanthropic role), the program’s configuration file (much
more useful), or even arguments passed to the role.
If you’re going to factor out common code into a role, why not go one step further and allow the parameterization of that code? When you apply a role to a class or an object, you can add arguments to the role to gain even more specific behavior.
For example, if you have a Loggable role, why not pass a
filehandle or the name of a log file to the role at composition time? If
you have a role that performs internationalization and localization, why
not pass the name of the string library or the language to use?
There are plenty of possibilities and dozens of potential patterns awaiting discovery and categorization.
Conclusion
Despite the heavy insistence of too many OO tutorials, inheritance isn’t the fundamental principle of object orientation. Polymorphism is. Supporting roles at the language level itself provides yet another way of promoting polymorphism and providing design tools to build better systems. (Yet polymorphism based strongly on class identity loses most of its power; it’s allomorphism that matters — nominal typing over structural typing.)
Thinking in terms of capabilities and named collections of behavior can help you divide your systems into smaller, self-contained entities. These don’t have to be classes themselves, nor do they necessarily have to have ancestor or descendant relationships with other entities in your system.
Decreased coupling, increased cohesion, improved code reuse, and more ubiquitous and automatic polymorphism are fine design goals. Roles encourage them.

An interesting discussion, to which I have rather questions than comments: is there any particular literature on which this entry is based? To what extent are roles implemented in Perl6 (I read that Perl6 is "on the way")? Is there any syntax specification for it (which I haven't found in few minutes; only the following page: http://dev.perl.org/perl6/doc/design/syn/S12.html containing sth on roles). Thanks.
You know, I'd argue that 'polymorphism' (especially the sort practiced by static, pseudo OO languages) has become such a debased term that it's better to think of the real essence of object orientation as late binding - the target object decides, at runtime, how to deal with a message.
Which puts Multimethods in a slightly weird place, but they're slightly weird anyway, so that's okay.
Piers, I thought of explaining it in terms of genericity, but C++ has abused that so much (along with the notion of "compile time") that polymorphism seemed a better word.
Hi chromatic
Are roles same as mixin's supported by Ruby? If not, what's the difference?
So we have the term role, used to constrain the behaviour of objects.
Since Terry Halpin (recently at Microsoft, now returned to Academia) created Object Role Modeling, is this terminology consistent with that?
chromatic--
Thanks for writing this. It's definitely a useful description of the functionality of roles. After reading, I do have a couple of questions that I hope you can help me with.
First, you say:
Would you be willing to expand on the relationship between types and roles? Is the functionality of types a subset of the functionality of roles, or are the two really synonyms?
Second, Synopsis 12 gives an example of using an anonymous subtype in a signature:
sub check_even (Num $even where { $^n % 2 == 0 }) {...}In your view, is this the same as declaring that
$evendoes theNumrole, declaring an anonymous role whose only parameter is constrained to be even, and then asserting that$evendoes that anonymous role? Is this an implementation issue or is dispatching types to roles central to the functionality you want roles to provide?Thanks for any feedback,
/au
For an entry point to literature on roles, you may have a look at
www.iam.unibe.ch/~scg/Research/Traits/
Prose, Ruby mixins are similar to roles in that they add methods to classes (or instances, but then there's an anonymous parent class added) but different in that they do not participate in the type system. That is, to my knowledge, there's no built-in system in Ruby by which you can specify that a class or object must have mixed in a particular named bundle of methods. There's duck typing, but it has the false cognate problem.
aufrank, the connection between types and roles is that all types are roles. That is, to declare a type, you declare a role (or a class, or...). A role is a named collection of behavior about which you can reason certain things. That's the same thing as a type, at least if you buy into nominal typing.
That's my reasoning anyway.
As to your second question, I say that the signature declares an anonymous role that
$evenmust perform. It's the job of the method dispatcher to check this. (In this case, it may be wise to check forNum-ness -- sorry -- before checking the subtype constraint.)I may eventually regret suggesting that the words "role" and "type" are somewhat synonymous here.
What method were you using to check them? I tested the
instanceofoperator and it works on classes I've extended and interfaces I've implemented. More on this in a moment.Recently, I was looking at Swing to build a simple GUI app in Java. One of the things recommended in Swing tutorials is the use of javax.swing.SwingUtilities.invokeLater() to asynchronously execute things on the GUI thread by adding them to a queue. invokeLater has a signature of
public static void invokeLater(Runnable doRun)Runnable is an interface, but you can't tell that by looking at invokeLater's method declaration.
To test instanceof's behavior, I wrote the following two objects. One of the objects implements Runnable and the other has a main thread to create an IRun object to test it using instanceof.
(The following text should contain tabs, but in the preview, something was still stripping them out, even inside preformatted text tags.)
As you can see,
doRun instanceof Runnablereturns the boolean value true, which is implicitly converted to a string bySystem.out.prinln().Powerlord, I just checked with Blackdown Java 1.3.1 (the latest version that I can convince to run on Linux/PPC unfortuantely), and you're right. My concern in the article was that you cannot use an interface name as a type in the signature of a method, but I just tested that and it worked correctly.
I still dislike the separation implied by
implementsand the abstractness of interfaces, but my other criticism was clearly wrong.