Where's the Bug?

Ovid on 2007-10-12T07:43:54

You know, there are plenty of modules out there which expect you to inherit from them to gain their functionality. Perhaps the worst offender is Exporter. Your non-OO modules are expected to inherit? Sheesh.

If you're writing an abstract base class, it makes perfect sense to use inheritance, but please stop and ask yourself if there's any other way you can provide the desired functionality. Particularly with multiple inheritance (MI), Perl's default method resolution order is awful (C3 method resolution allieves the pain but doesn't make it go away).

Of course, anything which forces MI should be viewed with great suspicion (I'm lookin' at you, Catalyst). MI is so well known to be problematic that many languages with OO facilities simply forbid it.

So, let's say I need a factory class, with class data, don't want to worry about missing methods (like Smalltalk and Sqeak) and want handy method generation.

Where's the bug?

  package Hate;

  # Hmm, better sort these so they're easy to read ...

  use base qw(
    Class::Accessor
    Class::BlackHole
    Class::Data::Inheritable
    Class::Factory
  );

Now how much of that behavior could you duplicate without forcing inheritance on people?


Exporter and inheriting

arc on 2007-10-12T12:21:06

It’s certainly common for non-OO modules to inherit from Exporter, and it matches the first example in the Exporter synopsis. But the second example shows an alternative:

package MyModule;
use Exporter qw<import>;
our @EXPORT_OK = qw<...>;
I recently changed a large codebase to use that approach instead of inheritance.

use Moose

Stevan on 2007-10-12T14:09:29

Well, you could duplicate all of this functionality and require no MI at all using Moose and the Class::MOP meta level (the only forced inheritance being Moose::Object, but even that is optional). This all too common abuse of inheritance was one of the main reasons I wrote Moose the way I did. Having the meta layer allows you to add behavior to classes which belong with the class and not with any object instances created by the class (your classic OO "seperation of concerns"). Some of this behavior could easily be implemented with Roles as well (or in a non-Moose world using Class::Trait).

As for the question ...

Wheres the bug?
Well, the first thing that I saw was this line from the Class::Blackhole docs.

Be sure to have Class::BlackHole be the absolute last item in your class's ISA list.
Which is then immeditely followed by this line:

This class will almost definitely not work right as part of any ISA tree that has multiple inheritance.
I hate to say it, but I think your "bug" is less a case for the evils of MI, and more a case of not RTFM.

- Stevan

Re:use Moose

Ovid on 2007-10-12T14:39:50

Heh. Point taken :)

It's also worth noting that two of those classes provide their own constructor. Hmm, which should I use? Can I use both? Must I use both? All the more reason to provide that functionality without inheritance.

Speaking of Moose (which I really like, though I've yet to have used it in a production environment, how's it's performance coming along?

Re:use Moose

Stevan on 2007-10-13T05:25:23

Speaking of Moose (which I really like, though I've yet to have used it in a production environment), how's it's performance coming along?
Well, the startup cost is still kind of high because we do so much in the compile phase, but if you are in a persistent environment that is a moot point. The runtime speed is basically "fast enough" for most people and making your class immutable only increases that.

The most basic accessors are actually faster than Class::Accessor, of course if you add type constraints and such it will slow it down some. Object construction can be a little expensive, but if you make your class's immutable then the constructor is inlined and that cost is greatly reduced.

- Stevan