Object-oriented design has faltered somewhat on the overuse of inheritance and class hierarchies at the expense of polymorphism and true typed genericity. Traits and roles form one great approach to solving those problems in an efficient, resuable, and powerful way. They’re available in Perl 6, of course — but they’re also available in Perl 5.
Curtis Poe’s and Stevan Little’s Class::Trait is
the most complete implementation of traits (or roles in Perl 6)
available on the CPAN today. I recently explored it for an afternoon.
Here’s what I found.
What are Traits
Traits come from two ideas. The first is that “traditional” OO systems, especially class-based, often have the severe limitation of encouraging inheritance and unwittingly discouraging other mechanisms of code reuse and genericity. The second is a research paper exploring this idea in the Smalltalk programming language.
A trait, also known as a role in Perl 6, is a discrete unit of behavior and state. It resembles a class in that sense, but you cannot instantiate a trait or role directly. Instead, you must compose it into a class. This provides code reuse unrelated to inheritance.
There’s more.
By defining traits, you can describe collections of behavior and state in terms of these traits. For example, your system may have traits such as Loggable, Stringable, and Persistable to describe behavior that many classes and objects must support. By specifying that the classes and objects you use support those traits–but not specifying how they do so–you can allow further decoupling and more flexibility in your design and use.
I realize that sounds very abstract, but think of it as a system more
concerned with the capabilities of objects and classes than their
parentage, where it’s more important that you can stringify something for
printing, whether it’s a complex object, an exception, a string, or a
number than that it is just a String, no more and no less.
Class::Trait uses Perl 5’s
flexible-but-rarely-dogmatic-about-anything object system to support traits
in a usable way.
How to Use Traits
To declare a trait, start to define a class as usual. You don’t need a
constructor, as you likely expect people to compose the trait into existing
classes which themselves have their own constructors. Do use
Class::Trait.
I wrote a Serializable trait which describes the necessary
behavior to serialize an object from memory to a string. It
provides one method, serialize(), which you can call
on an object to receive the string. It requires one method,
get_attributes() from the composing class, so that it can get
a data structure representing the object:
package Serializable;
use strict;
use warnings;
use Class::Trait 'base';
our @REQUIRES = 'get_attributes';
sub serialize
{
die "Empty serializer in Serializable; use a concrete implementation.\n";
}
1;
Note the base parameter passed to
Class::Trait. This registers the Serializable
trait. As you can see, the only explicit other metadata is
@REQUIRES, which contains a list of the names of all required
methods. This interface isn’t great, and it can cause problems with
advanced trait usage. There are hacks to get around it, but … ouch.
Using a trait is simple. I wrote two classes with slightly different
object styles, HashBased and ArrayBased. Yes, I
do like transparent examples. They both use a trait and provide the
get_attributes() method. (Not providing one is a compile-time
error.)
#!/usr/bin/perl
use strict;
use warnings;
package HashBased;
use strict;
use warnings;
use Class::Trait 'Serializable';
sub new
{
my ($class, %args) = @_;
bless \%args, $class;
}
sub get_attributes
{
return { %{ +shift } };
}
1;
package ArrayBased;
use strict;
use warnings;
use Class::Trait 'Serializable';
my %args_ids =
(
name => 0,
age => 1,
id => 2,
);
sub new
{
my $class = shift;
my @args;
while (@_ >= 2)
{
my ($name, $val) = splice( @_, 0, 2 );
$args[ $args_ids{ $name } ] = $val;
}
bless \@args, $class;
}
sub get_attributes
{
my $self = shift;
return { map { $_ => $self->[ $args_ids{ $_ } ] } keys %args_ids };
}
1;
When Perl compiles each class, it will load Serializable
from disk (if not already), then try to compose its methods into the class.
If there are failures, they will happen at compilation time. That’s a nice
benefit.
(Collisions cause a subroutine redefinition warning. This could be stronger. In Perl 6, it’s not an error to compose a role into a class that provides some of what the role provides. It would be an error if the role overrode the class’s methods.)
It’s easy to write code to demonstrate that the traits work… at least
except for the fatal serialize() method. Hold on; I’m going
somewhere with it.
my $hb = HashBased->new( name => 'Bob', age => 54, id => 200 );
my $ab = ArrayBased->new( name => 'Jim', age => 58, id => 100 );
print "HB: ", $hb->serialize(), "\n";
print "AB: ", $ab->serialize(), "\n";
After creating one object of each class, it should be possible to call
serialize() on them and get back the data in an appropriate
format. It should be, except that serialize() from
Serializable dies. Why? I intended it as an abstract base
trait. Yes, I am abusing Class::Trait, but that’s
what I do.
I wrote two other trait modules that inherit from
Serializable. The first uses Data::Dumper to
serialize the object’s data to a Perl data structure:
package ToPerl;
use strict;
use warnings;
use base 'Serializable';
use Data::Dumper;
our @REQUIRES = 'get_attributes';
sub serialize
{
my $self = shift;
my $atts = $self->get_attributes();
return Dumper( $atts );
}
1;
The second uses YAML to produce a YAML string:
package ToYAML;
use strict;
use warnings;
use base 'Serializable';
use YAML;
our @REQUIRES = 'get_attributes';
sub serialize
{
my $self = shift;
my $atts = $self->get_attributes();
return Dump( $atts );
}
1;
Note that there is a little bit of duplication in the
@REQUIRES line. I don’t like that, but this was the cleanest
solution.
Changing HashBased and ArrayBased to use these
traits is easy. Just change one line from:
use Class::Trait 'Serializable';
… to:
use Class::Trait 'ToPerl';
Now the example code should run correctly.
(There are other ways to solve this problem too. Another option is to
make Serializable a concrete role — that is, with no methods
you have to override — and then make it parallelizable by passing in some
sort of option for the serializer to use. That seems more difficult in this
simple case, but it’s a great technique for other problems.)
Traits aren’t just for compile time. You can change the composed trait
at run-time too. If Data::Dumper just isn’t working out for
HashBased, write:
Class::Trait->apply( HashBased => 'ToYAML' );
print "HB (YAML): ", $hb->serialize(), "\n";
Now serializing these objects will produce YAML strings.
You can also apply traits to individual objects:
Class::Trait->apply( $ab => 'ToYAML' );
print "AB (YAML): ", $ab->serialize(), "\n";
print "WB: ", $wb->serialize(), "\n";
Though the $ab object now serializes to YAML, the
$wb object of the same class serializes to Perl, just
as it did before.
This technique is immensely powerful, especially when you need to compose in data methods. I’ve used it successfully in a game to compose different View traits into my Model objects, for example.
How is the trait querying? It’s pretty good, but it has two slight
problems. To see if a class or an object can perform a role, use the
does() method:
print $wb->does( 'ToPerl' );
print HashBased->does( 'ToYAML' );
Of course, this ignores trait inheritance (and to be fair, I don’t know if the authors considered it; I didn’t bring it up until now). It also fails to catch the case where a class performs its own trait. I believe that every class implies the existence of a trait (or, more directly, roles are types and a class is a specialization of a role). This, sadly, fails:
print HashBased->does( 'HashBased' );
Perl 5.10 has a new method in the UNIVERSAL class called
DOES() that, well, does the same thing. It might be nice to
standardize on the capitalization there. (I don’t really like it, but I do
agree that it’s probably the right approach.)
Conclusion
These few drawbacks are easily solvable. Class::Trait does
much, much more. Even so, using its simplest features has the potential to
simplify your code immensely, while making it more generic and reusable and
powerful. Roles are one of the best features of Perl 6’s object system, and
Class::Trait does a fine job of making them available in Perl
5.
I highly recommend exploring this module and the concepts of traits and roles.

Great,
now I've got a clue what this Role stuff in perl6 is about. It seems to be worth considering not to inherit but to mix in and thats a good way. Thanks.
is traits like a kind interface from the java world?
michel: traits are considerably different from Java interfaces. One of the serious problems with interfaces is that they specify and interface by no implementation. Thus, you need to rewrite the implementation every time for every class heirarchy which uses the interface. You can get around this by trying to create small delegation classes, but then you have to write more code again and anythin which forces you to write more code is kind of missing the point.
With traits, the implementation is already provided. And unlike multiple inheritance or mixins, you have no ordering problems, so you can simply declare that your class provides certain traits and either include, exclude, or rename trait methods on the fly to provide the behaviors you need.
Could one just import the serializable method (really just a sub) into one's package using the conventional import techniques and have it work the same way as a 'trait'? I've never tried that but it seems like it would accomplish what you;re doing, unless perl's OO system purposely overlooks imported subs when scanning for methods.
Ryan, if you do it that way, you cannot use
does()to see if an object or class performs a given trait.You don't always get the right answer by using
can()either, because of homophonic methods.DogandTreeobjects both canbark(), but it's unlikely both methods have the same semantic meaning.Think of a trait as a named, cohesive collection of behaviors. Without the name or without the behavior, it's not a trait. That doesn't mean that whatever it is is useless, only that it's not a trait.
Let me play devil's advocate for a bit here so I can learn this better. Traits make sense to me but I can't come up with a solid reason why, yet. Let's take the above example. What advantage does it have over inheritance? If the modules used inheritance instead of traits as far as I can tell everything would work the same except that you wouldn't need the @REQUIRES lines. And ref() on an object would return a different answer.
I can see why a trait shouldn't be a full-blown class, because it doesn't need a constructor; you're never going to instantiate an instance from it. Like a Java interface. But Perl classes are incomplete to begin with; if you don't write a constructor, there isn't one. There's the usual bad taste in the mouth of multiple inheritance, but that's mainly due to diamond pattern problems with attributes, and Perl classes don't have instance attributes, only methods.
So if someone asks me why traits are better than multiple inheritance, I don't have a good answer besides, "it's considered a best practice" at the moment. Can someone help me out?
Ovid, michel: true, traits are "considerably different from Java interfaces", but, Java interfaces aren't supposed to allow for code reuse. That's what composition is for. Java interfaces only tell the runtime that certain objects provide certain methods. This is by design, not a shortcoming.
Seems like traits are interfaces + default implementation - headache of multiple inheritance.