Multiple Inheritance and Exporter

Ovid on 2009-09-29T09:00:07

We already know that multiple inheritance is bad. So I'm not surprised about the reported multiple inheritance Exporter bug. What I am surprised about is that this behavior still surprises people. Clearly more education is needed. So here's some education for you:

Multiple inheritance is wicked and wrong and you shouldn't use it!

It used to be the case that you might be able to make a weak argument for this abysmal practice. With roles, I'm hard-pressed to think of any excuses. Stop using multiple inheritance.

 

 

 

 

 

 

 

 

 

Given how many times I warn against absolute terms like "never" and "always," I'm on thin ice here.


Bit me the other day

leedo on 2009-09-29T15:19:49

I maintain an ancient codebase (uses Class:DBI and Exporter heavily) and ran into this recently. I had a hell of a time tracking it down and ended up just fixing it by not inheriting from Exporter. It was all pretty frustrating.

Design Concept Good! Implementation Bad!

dmaestro on 2009-10-01T13:42:14

You admit you are on thin ice, so allow me to pull you back before you break through.

Multiple Inheritance is a fundamental OO concept.

The only programming language I know that *safely* implements it is Eiffel.

Most people who get in trouble with MI don't realize the fundamental danger of method name conflicts, and how they must be resolved. That is the issue with the bug you reference (not in Exporter after all, but with Class::Accessor::Fast).

OOSC2 (Meyer, 1997) highlights two "interesting issues" at the very beginning of the chapter on MI:

* Feature renaming (feature = method/attribute)
* Repeated inheritance

These issues can be learned, and MI can be used properly. And they are not only "interesting," they are essential.

I agree this far: if you don't understand these issues, stay away from MI or don't cry when you get hurt!

The sad fact is that most current programming languages (including perl) give you the power without the safety. Think power [name your favorite woodworking tool] without guards. Unfortunately, no language can help a design that shoehorns in inheritance (of any sort) where it doesn't belong.

To the extent that Roles deals with the feature renaming issue (my understanding is, it does this well), it is a useful safety feature and should be used! As far as OO design is concerned, Roles is implementing a commonly needed and restricted form of inheritance, Structural Inheritance of Partially Deferred Classes (Meyer).

Roles is not an alternative to the design concept of Multiple Inheritance (good!), but to the generally unsafe (bad!) implementation of MI in common perl practice.

I have used multiple inheritance (carefully!) in both C++ and Perl with great success. Of course there was the time I found g++ had a bug in its handling of multiple dispatching ... ;-)

Re:Design Concept Good! Implementation Bad!

Ovid on 2009-10-01T14:13:05

Roles is not an alternative to the design concept of Multiple Inheritance

What's the design concept of Multiple Inheritance?

The problem with OO as it's usually conceived is that classes are used both for their responsibilities -- which tends to make the classes larger -- and for behavioral reuse (frequently via inheritance). When you want to share behavior in a procedural module, we know that it's usually a bad idea to have modules automatically pollute your code with a bunch of functions you didn't ask to import. You should ask for them explicitly (via @IMPORT_OK if using Exporter). Thus, we generally find it's better to allow people to pull in a subset of desired behavior (e.g., you usually don't want all of the functions which List::Util or Scalar::Util provide). Thus, behavioral reuse implies that we want smaller amounts of code in the behaviors we're sharing.

As a result, classes have long had a well-known larger/smaller tension in their dual nature of responsibility/reuse. The only reason I've ever seen anyone use MI is for behavioral reuse, but since it's constantly abused and so easy to get wrong and trivial to create examples which are difficult, if not impossible, to work properly under MI, why bother? There's an excellent reason why many excellent language designers forbid MI. They want to separate the responsibility and code reuse nature of classes. Java tried to do this with interface. Ruby tried to do this with mixins (a technique originally found in a LISP variant). If so many experienced language designers are trying to find a better way to handle this problem -- a problem which we've struggled with for FOUR DECADES -- then why on earth should we mere mortals suggest such a buggy and fallible technique? Since there are clearly superior alternatives available (roles, in this case), why not use them? You get all of the benefits of MI and none of the drawbacks.

I repeat: roles give you all of the benefits (and more!) of multiple inheritance and none of the drawbacks. So why cripple yourself and others? Roles are easy to get right and multiple inheritance is easy to get wrong. It's a no-brainer.

not to mention...

rjbs on 2009-10-17T00:46:54

...why would you use Exporter when Sub::Exporter is right there? I mean, it's practically a parameterized role itself...