The Case for Multiple-Inheritance

On Sun, 2007-10-07 at 05:57 +0900, Sylvain J. wrote:

That’s because, since mixins aren’t abstract (like interfaces), they
represent a limited form of MI. The limitations on Ruby modules only
mitigate the issues inherent in MI, not eliminate them. But … do you
really want to make the issues worse by relaxing the restrictions?

I think that programmers should have the right to do their own decisions.
If they don’t feel like using MI, fine. But an anti-MI religion.

I’m just concerned that people are asking for them without being willing
to accept the consequences of MI in a dynamic language (even after
getting a small taste of them via modules and not liking them).

I already used MI in C++ (and yes, for “is-a” relations, cut the
you-should-not-use-inheritance crap) and I have seen people using MI in
very elegant ways.

I’ve used (and use) it in C++ occasionally, but C++'s nature makes for a
very different set of trade-offs than Ruby. Since you weren’t very
happy with linearization, is there a different conflict resolution
technique suitable to a dynamic language which you would prefer to see
in a fully MI Ruby?

-mental

On 10/6/07, Sylvain J. [email protected] wrote:

If they don’t feel like using MI, fine. But an anti-MI religion.
They do, but not all of those ways are possible in Ruby. C++ (as
noted) is really bad at delegation; Ruby excels at it. C++ is better
for MI (because of the explicit support and such) than Ruby, even with
mixins. But Ruby tends to be more compact and useful for most models.

All powerful programming tools have drawbacks. The question is whether or
not you gain more by taking the risk of using them than by avoiding them.
I already used MI in C++ (and yes, for “is-a” relations, cut the
you-should-not-use-inheritance crap) and I have seen people using MI in
very elegant ways. It is useful in some cases, can be avoided in others,
but it is useful sometimes.

I’d say it should be avoided 98% of the time (MI). Inheritance in
general, is probably overused by at least 50%. (Most is-a
relationships aren’t useful ones and are caused by people who aren’t
used to thinking of OOP in other terms. I’m not suggesting this for
you, but is-a has a lot of implications that has-a doesn’t. Therefore,
it’s not “you-should-not-use-inheritance crap”, it’s sound advice from
years in the industry.)

-austin

On Sun, 2007-10-07 at 07:18 +0900, Austin Z. wrote:

What Mentalguy’s suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.

Also, even when you have a true “is-a” relationship, “is-a” in the class
domain doesn’t always correspond to “is-a” in the domain being modeled.

This is because the requirements of dynamic method dispatch introduce
additional constraints on subclassing which violate some of the laws a
simple transitive relationship like “is-a” might reasonably be expected
to obey.

Single-inheritance is an obvious example of one such constraint, but
allowing multiple inheritance simply means replacing that constraint
with others.

-mental

On Sun, 2007-10-07 at 08:38 +0900, MenTaLguY wrote:

This is because the requirements of dynamic method dispatch introduce
additional constraints on subclassing which violate some of the laws a
simple transitive relationship like “is-a” might reasonably be expected
to obey.

Actually, I guess this applies to having methods in general. The
classic ellipse-versus-circle thing is a great example: “is-a” in the
model domain (shapes) is exactly the opposite from “is-a” in the class
domain, at least in terms of Liskov substitutability.

-mental

On 10/6/07, Sylvain J. [email protected] wrote:

On Saturday 06 October 2007, MenTaLguY wrote:

On Sat, 2007-10-06 at 19:17 +0900, Sylvain J. wrote:

The bottom line being: if I was to not use inheritance for my
purposes, I would have to reinvent the “inheritance wheel”.
(The one sticking point I would occasionally hit was delegation, as
See the top of my previous post: you’re talking about duck typing, which
does not apply in my case since I need to manipulate the models, and I
need (for instance) to be able to check that an objet is kind_of? a
particular class/module.

You could, of course, override #kind_of? to do what you want.

Would you be interested in working a simple example, to see if there are
other relationships besides “is-a” in your model?
Sorry, I don’t think I have the time/motivation for that. I don’t mind
discussion (and I don’t mind being proved wrong if I learn something in
the process), but I thought a lot about it and yes I’m using inheritance
for ‘is-a’ relations. I’m finishing my PhD write now and I don’t have a
lot of time for that.

What Mentalguy’s suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.

-austin

On Sun, 2007-07-10 at 07:16 +0900, Austin Z. wrote:

You can, sort of. However, I don’t get the current mania for appending
methods to the class-object and the instance-object at the same time.

It’s nice for metaprogramming. However, in many cases these things
should really have been singleton factory objects, rather than classes.

I can see limited use for it; I’m more referring to what seems to be
an absolute mania about it. (That is, wanting to do it EVERY TIME.)

This goes back to my rant about “just because you have a language
feature doesn’t mean you should use it all the time”.

I have similar thoughts about Lisp-alikes and macros. C++ and
templates. Haskell and combinators. Etc., etc., etc. ad nauseum.

On Oct 6, 2:51 pm, MenTaLguY [email protected] wrote:

cake and eat it too).
No no. It’s not the linearization. The linearzation is what so great
about how Ruby does inheritance --it’s what makes the MI (using
mixins) manageable! Yes, there’s some debate about including modules
more than once in the same hierarchy. I’ve never really run into a
case where I’ve needed that, but I suppose it’s possible. But it
doesn’t really matter. The important thing is just concretely knowing
where in the hierarchy a class/module is. The linearization provides
that.

The thing is (and maybe this will help clarify how I see this) the
more I work with Ruby the more I feel like the best code is always of
the form:

module M

end

module X

end

module Y

end

class Z
extend M
include X
include Y
end

Such that everything is a module and classes are always just
compositions of modules. This provides maximum code reusability.

However, as one does this it becomes clear that it’s more involved
then it needs to be. One has to name many more abstractions than the
model you have in mind actually has. For instance, because you have
the heart of a class in a module, you get something like:

module MyClassModule

class MyClass
include MyClassModule

and also b/c you have to define meta-modules separately for use with
#extend.

But if we instead remove the distinction between module and class,
i.e. one can include a class or subclass a module, then these extra
abstractions become completely unnecessary --our code can remain just
as we are acustomed to coding it, but we also have full code re-
usability at our disposal if wish to use it.

T.

Delegation does not mean duck typing. You are confusing your
terminology.
Tell me if I’m wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don’t think as ‘my type is
defined as what messages I’m able to answer to’, then you cannot replace
one object with one delegator since they won’t have the same type.

On Sun, 2007-07-10 at 07:21 +0900, Austin Z. wrote:

I’d say it should be avoided 98% of the time (MI). Inheritance in
general, is probably overused by at least 50%. (Most is-a
relationships aren’t useful ones and are caused by people who aren’t
used to thinking of OOP in other terms. I’m not suggesting this for
you, but is-a has a lot of implications that has-a doesn’t. Therefore,
it’s not “you-should-not-use-inheritance crap”, it’s sound advice from
years in the industry.)

Going back to the original model that sparked this: inheritance reflects
an “is-a” relationship. Someone wants to make an application object
that:

 1. IS-A Linux application; and
 2. IS-A video player application.

What stymies me here is… exactly which runtime is this going to be
running in where you can plug in a Linux application, a Windows
application or a … Symbian, say, application and need the transparent
dispatching to the right functionality? Just the “LinuxApplication”
class by itself makes me suspicious of the model behind it: what useful
abstraction do you get from this that you don’t get from an
“Application” that has a “POSIXFileSystem” (instead of “NTFSFileSystem”)
and a “LinuxSecurityModel” (instead of “NTSecurityModel”) or whatever?
Just this core has me scratching my head.

Adding the “VideoPlayerApplication” to the mix only raises even more
questions, all related, once again, to the nature of a runtime where you
need transparent dispatching to a VideoPlayerApplication over … what,
exactly? A VideoRecorderApplication? An AudioPlayerApplication? A
RealTimeAutomobileAssembleyApplication?

Basically, I’m failing to see any useful “IS-A” relationships at all in
the original poster’s model barring an awfully convoluted run-time with
some questionable approaches to things built into it. What he’s viewing
as inheritance (IS-A) situations to me look like composition (HAS-A)
situations.

And this is what strikes me about most C++ code: people using IS-A left,
right and centre because that’s what the language supports best; C++ is
positively lousy at supporting HAS-A relationships (and Java isn’t much
better).

On Oct 6, 10:13 pm, “Michael T. Richter” [email protected] wrote:

an “is-a” relationship. Someone wants to make an application object
abstraction do you get from this that you don’t get from an
Basically, I’m failing to see any useful “IS-A” relationships at all in
the original poster’s model barring an awfully convoluted run-time with
some questionable approaches to things built into it. What he’s viewing
as inheritance (IS-A) situations to me look like composition (HAS-A)
situations.

And this is what strikes me about most C++ code: people using IS-A left,
right and centre because that’s what the language supports best; C++ is
positively lousy at supporting HAS-A relationships (and Java isn’t much
better).

You’re assuming too much from the names. I’m just working on improving
Launchy to add a video player launcher, in the process I worked on the
overall code to make it more OOP.

http://rubyforge.org/projects/copiousfreetime/

T.

You could, of course, override #kind_of? to do what you want.
Yes. I could build a MI-like pattern from scratch. I does not seem to be
the “right way” …

What Mentalguy’s suggesting is that the relationships may not be best
modeled as an is-a relationship, as I suggested to Trans early on in
the discussion. Is-a relationships are very tightly coupled and should
only be used where such a tight relationship is warranted.
I’m modelling activities (moving to a point, moving the camera, taking a
picture at a location, this kind of things). To keep things manageable,
use the inheritance relationship to express that we have abstract
actions
(the generic MoveTo(x, y)) and implementation of those actions
(MyWayToMoveTo(x, y)).

I need to reason about these relationships because you can use a
specific
movement method in place of a generic one, but you can’t use a
TakePictureNow activity in place of a MoveTo(x, y). However, you could
use
a TakePictureAt(x, y) in place of a MoveTo(x, y) since it is both
modelling a MoveTo(x, y) and a TakePicture.

In most cases, it is not a problem: I represent the fact that
TakePictureAt(x, y) is in fact a sequence of MoveTo(x, y) and a
TakePictureNow. It is therefore possible to repesent the thing with
single
inheritance. But there are cases where this kind of separation cannot be
done – hence the need for one class to be an implementation of multiple
models.

Now: why classes ? Some code is tied to the various activities (code to
start the activity, to stop it, handlers when some event appear) and I
do
want the children models to inherit the methods of their parent models.

I actuall started to write parts of my framework without using method
dispatch and inheritance. It began quickly horrible, so I rewrote it
using
it. I’m more than happy now. I would just be happier with MI.

Sylvain

On 10/6/07, MenTaLguY [email protected] wrote:

On Sun, 2007-10-07 at 08:38 +0900, MenTaLguY wrote:

Actually, I guess this applies to having methods in general. The

classic ellipse-versus-circle thing is a great example: “is-a” in the
model domain (shapes) is exactly the opposite from “is-a” in the class
domain, at least in terms of Liskov substitutability.

It’s really unfortunately that the whole “is-a” idea got introduced into
OOP, because it’s not sufficient to represent inheritance. The example
of
shapes is a good one and worth emphasizing:

Square < Rectangle

or

Rectangle < Square

?

For most people, real-world intuition leans toward the first one.
However,
try writing a unit test for Rectangle where you can vary the height and
width independently, then substitute a Square. It doesn’t work. The
implication of the Liskov Substitution Principle (LSP) is that we really
need to talk about “behaves as a” relationships, not structural “is a”
relationships.

There is one other interesting point about this. LSP is context
dependent;
it depends on the “client’s” expectations for behavior. While Square <
Rectangle fails for the test “client” that wants to vary the height and
width, it probably works just fine for another client that just wants to
use
read-only methods like #draw() to render shapes.

dean

Tell me if I’m wrong, but from a type point of view, delegation is
^^^^^^^^^^^^^^^^^^^^^^^^^
What? Half of the Design Patterns book is about how to use
composition & delegation instead of inheritance.
I don’t say that delegation does not exist outside duck-typing. And I
never
said that only inheritance is usable from an engineering point of
view.

However, the “model(a) == model(b)” comparison does NOT cooperate well
with
delegation unless your definition of model is the duck-typing one (for
instance if you define the type as the class of the object).

Sylvain

On 10/7/07, Sylvain J. [email protected] wrote:

Delegation does not mean duck typing. You are confusing your
terminology.
Tell me if I’m wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don’t think as ‘my type is
defined as what messages I’m able to answer to’, then you cannot replace
one object with one delegator since they won’t have the same type.

Sylvain J.

What? Half of the Design Patterns book is about how to use
composition & delegation instead of inheritance.

Pat

On 10/6/07, Trans [email protected] wrote:

include X
include Y

end

Such that everything is a module and classes are always just
compositions of modules. This provides maximum code reusability.

No, actually, it doesn’t provide maximum code reusability. Ruby supports
a rich vocabulary and you’re choosing to use a form that, while clearly
useful in some cases, is not right.

Modules in Ruby are useful for adding new “generic” class capabilities.

Enumerable is a way of using the ability to iterate over a collection
object (and a collection is defined by #each, which goes through the
state values of the object in turn). It’s like a Java interface except
that you don’t need to reimplement each and every piece of the interface
because the interface is always expressed in terms of #each.

Transaction::Simple is a way of adding “extra” state to an object that
allows it to be versioned in memory. All of that extra state is managed
by the methods defined in the module.

What you’re talking about is on par with components and graphical
development. They can work in simple cases, but the moment you start
having complex cases, you start realizing that the code is becoming a
bit more complex to integrate that way. You can either refactor your
code, or start looking at ways that the language can change to
accomodate your model’s inflexibility.

Modules aren’t really meant for providing state that the object depends
on normally. They’re extra. So you should be working with classes that
have implementations; you should refactor out common functionality (and
I mean REALLY common functionality) into modules; you should have a
class hierarchy if it’s appropriate; you should compose multiple objects
into a single object where appropriate.

I’ll put it clearly: if I were running a Ruby consultancy and a
programmer of mine used your methodology for everything (e.g., classes
defined by composing modules), I would not keep that programmer on. It’s
a bad style. Sorry.

There are legitimate arguments (made by matju) for the unification of
class and module; your case is not one of them.

-austin

On 10/7/07, Sylvain J. [email protected] wrote:

Delegation does not mean duck typing. You are confusing your
terminology.
Tell me if I’m wrong, but from a type point of view, delegation is only
usable in the context of duck-typing. If you don’t think as ‘my type is
defined as what messages I’m able to answer to’, then you cannot replace
one object with one delegator since they won’t have the same type.

It’s possible to use delegation with non-dynamic languages. Hard, but
possible.

-austin

On 10/7/07, Austin Z. [email protected] wrote:

extend M

a bad style. Sorry.
Agreed. That looks painful.

Pat

On Oct 7, 8:42 am, “Austin Z.” [email protected] wrote:

No, actually, it doesn’t provide maximum code reusability. Ruby supports
a rich vocabulary and you’re choosing to use a form that, while clearly
useful in some cases, is not right.

Yes it does, b/c if methods are placed in a class, the only reuse if
via a subclass. If they are in a module they are not limited by that
constraint. I haven’t lost anything at all by putting all behavior in
modules, I have only gained. The approach is a superset.

I seem to be the only one who ever gives code examples. How about you
show me an example of how you’d do it?

by the methods defined in the module.
have implementations; you should re-factor out common functionality (and
I mean REALLY common functionality) into modules; you should have a
class hierarchy if it’s appropriate; you should compose multiple objects
into a single object where appropriate.

You are constraining the use of modules according to your own view of
how they “should” be used, and in the process I think you’re missing
my point. You’ve created an artificial separation between forms of
code encapsulation because, you reason to yourself, one is for
handling state and one if for generic capabilities. But that is
putting a hard line in a soft world. There really is no need to make
that hard distinction. If a coder, like yourself, wants to do that you
can do so just as easily with a single form of encapsulation. However
the world is not always so clear cut. Modules sometimes deal with
state. If we were to follow your logic all the way through, then
modules shouldn’t be allowed to address state at all. Would we want
that? But if we follow my logic all the way through, we have lost
nothing. We have only gained. Whether someone else avoids it or shoots
themselves in the foot with it is their own problem. I see very good
ways to put the new capabilities to elegant use.

What we’re talking about here ultimately is just a matter of coupling
between components. You don’t think modules should have anything to do
with this and that we should all be using classes and composition
instead b/c it doesn’t couple the components as tightly together. But
I think that’s incorrect. Modules are a perfectly valid way of
creating components, and coupling behaviors. As along as you avoid
shared state (and by that I mean specifically instance vars) then the
coupling is being managed quite well. The trade off is just a matter
of managing a greater potential for method name clashes, but you save
yourself the complexities of method routing. You pick your poison and
I’ll pick mine. In the mean time your insistence on it being your way
or no way is really unfortunate. You might not agree with my
techniques but why are you trying to stop me from doing it? Hey, I’m
all “Burger King” here!

I’ll put it clearly: if I were running a Ruby consultancy and a
programmer of mine used your methodology for everything (e.g., classes
defined by composing modules), I would not keep that programmer on. It’s
a bad style. Sorry.

Clearly there is a reason you aren’t running a consultancy.

T.

On 10/7/07, Sylvain J. [email protected] wrote:

Sylvain

I really really wish you would provide a quick example of what you
actually want. I know you’re quite busy, but you’ve managed to find
plenty of time for this discussion.

Here’s something off the top of my head, that allows you to reason
about “types” without needing MI.

module LinuxApplication; end
module VideoApplication; end
module WindowsApplication; end

class LinuxVideoApplication
include LinuxApplication
include VideoApplication
end

class WindowsVideoApplication
include WindowsApplication
include VideoApplication
end

def only_linux_video_please(app)
raise “Must be a LinuxVideoApplication” unless LinuxApplication ===
app && VideoApplication === app
end

On 10/7/07, Trans [email protected] wrote:

the form:

Bahahahahahahahahaha!
class Y; end

You misunderstand me. What looks painful to me is having the behavior
of a class tucked away behind 15 different modules, instead of being
right in the class where it belongs.

Pat