in reply to Re^2: OO: how to make a generic sub (use roles or inheritance)
in thread OO: how to make a generic sub

"I learned OO on Java, then graduated to C++. So, my first thought on reading the above was: roles here are analogous to Java interfaces — except that a role contains an implementation whereas an interface does not."

Bingo!

Or rather, roles may provide implementation. In the Roles::Jumper role, the role specifies that the name and jump methods are part of the interface, but only provides an implementation for jump; it requires classes that consume the role to provide name.

"But then I wondered: how is this different from good old (possibly multiple) inheritance?"

Conceptually it's similar, but roles offer some protection against some of the problems associated with multiple inheritance.

The classic one is when you inherit methods of the same name from multiple parents. With a good implementation of roles (and in this context, Role::Tiny, Moo::Role, Mouse::Role and Moose::Role are all "good") you'll get an exception raised if your role composition causes a method conflict.

The example in the Moose docs is that role Fragile and role Dancer might both have a break method. If you want to build a class FragileDancer that composes both of those roles, then the way to avoid raising that exception is for FragileDancer itself to provide a break method (which might call Fragile::break or Dancer::break or both), and thus resolve the conflict.

If Dancer and Fragile had been classes, and FragileDancer multi-inherited from both, then exactly what $fragiledancer->break did would be a bit of a mystery. It would depend on the order of the @ISA array. If you had some code that did:

$thing->break if $thing->isa("Fragile");

... then you might be surprised if $thing suddenly started break-dancing.

Roles force the FragileDancer class to resolve the conflict in a way that makes sense to that class. It forces the person writing FragileDancer to actually think about the break method; realise that it's a conflict, and realise that it's important to document what it does in FragileDancer because it might differ from the expectations of people familiar with Fragile and Dancer.

Roles can also be used as a kind of "tagging" for classes. For example, say you've got a bunch of classes which inherit from MyApp::Plugin. Maybe some of the plugins need to be activated at stage 1 of your app, and some need to be activated at stage 2. In that case, you can use empty roles to tag which stage each plugin needs to be activated at.

package MyApp::Plugin::Stage1 { use Role::Tiny }; package MyApp::Plugin::Stage2 { use Role::Tiny }; package MyApp::Plugin::Frobnicate { use Role::Tiny::With; with "MyApp::Plugin::Stage2"; } ...; foreach my $plugin ($self->plugins) { # The "DOES" method is like "isa", but checks roles # instead of checking inheritance. # if ($plugin->DOES("MyApp::Plugin::Stage2")) { $plugin->activate($self); } }

Some people have likened the difference between role composition and inheritance to horizontal versus vertical. If you have an inheritance hierarchy, roles are like pulling extra functionality in from the sides.

"In my present state of ignorance, the two techniques you illustrate look like syntactic variations on an identical theme, and I can’t see any reason to prefer one over the other."

In the simple pets example, it does make little difference whether you use roles or inheritance. However, in a more complex application where you want a class to be composed of functionality from several different packages, composing multiple roles generally has fewer gotchas than multiple inheritance does.

Update: I added some more stuff above about FragileDancer.

package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

Replies are listed 'Best First'.
Re^4: OO: how to make a generic sub (use roles or inheritance)
by Athanasius (Archbishop) on Jun 27, 2013 at 13:50 UTC

    Thanks for the excellent explanation.

    I now see the problem with multiple inheritance. If my FragileDancer class has use base qw( Fragile Dancer ); then a call to break() produces damage; but if the class has use base qw( Dancer Fragile ); then break() produces dancing. And no warning that a method name conflict has been encountered.

    So, I can see how roles could be useful here. But my experiments with Role::Tiny have so far been less than encouraging. :-( My first thought was:

    package FragileDancer { use Role::Tiny::With; with "Fragile"; with "Dancer"; ...

    but that turns out to be a documented way to resolve the conflict — it silently gives Fragile behaviour to break() because the Fragile role is listed first. So, it’s no different to the scenario with multiple inheritance. To get an exception, it seems I have to write:

    package FragileDancer { use Role::Tiny::With; with 'Fragile', 'Dancer'; ...

    which produces:

    Due to a method name conflict between roles 'Dancer and Fragile', the +method 'break' must be implemented by 'FragileDancer' at C:\Perl\lib\ +perl5/Role/Tiny.pm line 183.

    So far, so good. But when I implement that FragileDancer::break method as required, it makes no difference: the exception is generated exactly as before.

    What am I missing here?

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      This appears to be a bug. Your example works perfectly switching from Role::Tiny to Moose::Role. I'll ask around and update this node with what I find out.

      Update: yes, it's a massive bug with a one line fix (an eight character fix, actually). I'm shocked and appalled that such a clear problem exists in Role-Tiny. I've pushed a test case and fix to the Role-Tiny repository, but don't have the necessary PAUSE permissions to release a quick fix to CPAN. I'll try to badger somebody who does into getting it released quickly.

      Update (2.5 weeks later): Role::Tiny 1.003000 is out and fixes this issue.

      package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name