Perl Roles Versus Inheritance

| 1 Comment

In The Why of Perl Roles I explained some of the motivations behind the inclusion of roles in Perl 6 and their implementation in Perl 5 through Moose.

One common question is "How do roles differ from subclasses?" (where "subclassing" means "inheriting state and behavior from a parent class" as in C++, Java, et cetera).

Reddit commenter netytan pointed out that plenty of OO research literature uses the word "inheritance" in a broader sense, but I'm limiting it here to the concrete behavior by which you can specialize a more general class and take on or override its behavior. Any poor academics who run across this, please feel free to bite your lip because I'm about to tell some lies to help explain things to people who may not read your papers for fun.

Features of Inheritance

Suppose you write a class:

class Parent
{
    method wipe_nose { ... }
    method tie_shoes { ... }
}

You've declared a type, Parent, which has a specific interface (methods wipe_nose and tie_shoes) with specific signatures (zero-arity methods, in the absence of other information such as language support for slurpy parameters or mandatory signature checking), and an implicit semantic meaning for the term Parent. That is, you've modeled an entity representing some ideal. Whenever you refer to a Parent in your code, you mean this entity. Whenever you invoke the wipe_nose method on anything you treat as a Parent, you have specific behavior in mind -- the wipe_nose it makes sense for a Parent to do.

Suppose you extend this Parent by subclassing it (what I refer to here as "inheritance"):

class Child extends Parent
{
    method wipe_nose   { ... }
    method play_in_mud { ... }
}

This declares another type, Child, which behaves as a Parent (revealing uncomfortable semantic properties of my naming system here). In theory, anywhere I can use a Parent appropriately, I can use a Child appropriately. (The Liskov Substitution Principle applies.)

The converse is not true; a Child is a specialization of a Parent. You can see this in that the Child class defines another method, play_in_mud, which is not present in Parent. Parents can't play in the mud, lest they get their shoes dirty. (Of course my parents still think of me as their child, and I can take off my shoes anytime I want -- so the code example isn't as wrong as it could be. Real life is taxonomic weird.)

You've probably also noticed that the Child does not declare a tie_shoes method. The Parent implementation of tie_shoes suffices.

Even this simple example implies several features of this type of inheritance:

  • Classes define types which represent named collections of state and behavior. (Type declarations.)
  • Classes can extend or specialize other classes. (Subtyping -- though see Contra vs. Covariance for some debate over what languages can and should provide.)
  • Classes can include behavior from superclasses or reimplement behavior. (Code reuse.)

In my mind, this type of inheritance conflates two separate concerns: type relationships and code reuse.

When Inheritance Attacks

Problems arise when you want one or the other but not both (or at least not in the way the language supports).

Suppose you want to write a Guardian class which performs the same behaviors as the Parent class:

class Guardian
{
    method wipe_nose { ... }
    method tie_shoes { ... }
}

How do you express that a Guardian is equivalent and semantically substitutable for a Parent everywhere (at least in terms of the behavior described here)?

If your language supports this, and if you have the ability to modify the declaration and use of Parent, and if you can reify the results to something concrete, you could extract Parent into an interface like Java supports -- perhaps ICanHasChild.

What if the implementation of wipe_nose is the same between Parent and Guardian? You could subclass Parent to create Guardian and inherit the implementation of those two methods, but then Child has to subclass Guardian -- and speaking of weird taxonomic relationships, that hierarchy doesn't make sense either.

Then there's multiple inheritance and abstract base classes and other approaches to try to minimize all of the complexity of the conflation of these two separate ideas.

The Role Separation of Concerns

The important attribute of the Guardian type is that a Guardian can act like a Parent. It supports the same behaviors that a Parent supports. It may need to reuse some implementation from the Parent, but it's not clear that it has a hierarchical taxonomic relationship to a Parent.

Imagine instead if you could write:

class Guardian does Parent
{
    method wipe_nose { ... }
    method tie_shoes { ... }
}

Presuming you have a role-based type system, every place in your code that checks for a Parent object can use a Guardian object because the wipe_nose method called on either a Parent or Guardian object means "Grab a tissue, then hang on to your kid."

Alternately, you can slice all of these behaviors into more meaningful and smaller units of behavior: GoodHygiene could include the cover_your_mouth method and the wipe_nose and the brush_teeth methods. Perhaps they act on the invocant by default, so you can compose that role into any Person class -- but perhaps there's a role which represents performing good hygiene principles on a kid. Perhaps not. It depends.

Perhaps your Parent is also the Guardian for a grandparent. Perhaps your Parent is the Child of a grandparent. Perhaps your Child does babysitting for even younger children.

How do you model those relationships with an inheritance hierarchy?

The important question is not "How do these entities relate to each other in a static taxonomy?" but rather "What do you do?"

Next time: roles versus duck-typing.

1 Comment

I don't really follow how this is inherently better than classic OO besides the fact that it allows better MI...

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on May 1, 2009 3:30 PM.

The Why of Perl Roles was the previous entry in this blog.

Perl Roles Versus Duck Typing is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?