The Case for Multiple-Inheritance

N

Neil Wilson

It is a bunch of rules that attempts to resolve name conflicts,
parameter mismatches and dispatch problems all of which pop up when
modules collide in the same namespace.

The classic one is if you inherit 'foo' from two parent classes, what
happens when you call 'super'?
 
T

tho_mica_l

It's funny because a lot of people on this list have a hard time
wrapping their head around duck-typing :)

I wouldn't say they have a hard time of wrapping their head around it
--
although I find this metaphor quite interesting. Maybe they rather
think
of duck-typing as informal interfaces, but let's not call it this way
and give it a funky name instead, without having the benefits of
typing/formal interfaces.
an object's type is simply the messages it handles. Classes are
mostly an implementation detail, i.e. an easy way to construct
objects, as far as the interpretter is concerned.

I'd be very interested how you'd argue against MI from your point of
view?
 
P

Pat Maddox

I wouldn't say they have a hard time of wrapping their head around it

Search the list archives a little bit and you'll probably change your
tune. One related thing that comes up from time to time is people
wanting to create "abstract" classes. Basically they'll write a class
with a few methods that throw an error saying "You need to define this
method!" This stems from a basic misunderstanding of Ruby's
message-passing mechanism.
--
although I find this metaphor quite interesting. Maybe they rather
think
of duck-typing as informal interfaces, but let's not call it this way
and give it a funky name instead, without having the benefits of
typing/formal interfaces.


I'd be very interested how you'd argue against MI from your point of
view?

To be honest, I simply wouldn't. I don't find this relatively
low-level debate particularly interesting. I also admit there are
many people more knowledgeable that can give you more insightful
commentary than I could. I chimed in because it seemed to me like
Sylvain was having trouble with Ruby's implementation when it turns
out he was really missing an abstraction.

Pat
 
T

tho_mica_l

Should A's methods or B's methods break when called on an instance of

IMHO MI doesn't necessarily imply that every class goes well with
every other class and it's up to the developer to handle name clashes
and the like the same way developers have to make sure they don't, eg,
add a number and a hash. Languages like eiffel provide facilities to
rename methods for this.

While I agree that Ruby's modules/mixins can handle well many
situations where one would rely on MI eg in Eiffel and also that MI is
likely to unnecessarily increase the code's complexity to a degree of
unmaintainability, I can still remeber a (very) few situations when it
would have been handy. Eg with mixins, AFAIK you can't include class
methods and object methods at the same time, can you? Maybe I'm wrong
in this respect.
 
P

Pat Maddox

It is a bunch of rules that attempts to resolve name conflicts,
parameter mismatches and dispatch problems all of which pop up when
modules collide in the same namespace.

The classic one is if you inherit 'foo' from two parent classes, what
happens when you call 'super'?

Of course, you're not restricted to calling super and hoping
everything works out:

http://blog.jayfields.com/2007/08/ruby-calling-methods-of-specific.html

Instead of having a "bunch of rules that attempt to resolve name
conflicts", I would prefer to have simple, sensible default behavior
(i.e. the most recently included module's method is called) and have
the control to override it when necessary.

Pat
 
S

Sylvain Joyeux

No, you are stuck in a mindset and you have mapped that mindset onto
classes as though they are the only modelling tool available. There
are lots of other ways of modelling domains.
Well, I have to manipulate both "models" and "instances of these models". I
could re-write a whole type system -- that's how people doing that from
C++ do because of the lack of introspection. Instead, I decided to use the
model/instance of Ruby. Why ? Because I can *build* models (i.e. add
methods, inherit methods from parent models, in short: walk the
inheritance chain) and then ask ruby to build objects from those models.
It's not only about state, it's also about sharing code and inheriting
properties from a *code* point of view. And this is called the
class/object relationship.

The bottom line being: if I was to *not* use inheritance for my purposes, I
would have to reinvent the "inheritance wheel".

What I'm stuck with is having "first class models" (classes) and "second
class models" (modules). Know what ? What I gained by using Ruby and the
single inheritance *still* outperfoms the bunch of code I would have had
to write without them. Still, I'd like to have MI :p

Sylvain
 
T

Trans

Well, I have to manipulate both "models" and "instances of these models". I
could re-write a whole type system -- that's how people doing that from
C++ do because of the lack of introspection. Instead, I decided to use the
model/instance of Ruby. Why ? Because I can *build* models (i.e. add
methods, inherit methods from parent models, in short: walk the
inheritance chain) and then ask ruby to build objects from those models.
It's not only about state, it's also about sharing code and inheriting
properties from a *code* point of view. And this is called the
class/object relationship.

The bottom line being: if I was to *not* use inheritance for my purposes, I
would have to reinvent the "inheritance wheel".
Exactly!

What I'm stuck with is having "first class models" (classes) and "second
class models" (modules). Know what ? What I gained by using Ruby and the
single inheritance *still* outperfoms the bunch of code I would have had
to write without them. Still, I'd like to have MI :p

I so agree. I think Ruby's design has made class modeling wonderfully
straight forward and more useful. We should be happy that we don't
need to use more complicated techinquies, like composition, as much!
It strikes me that the advocates of composisiton are also the ones
with c++/java backgrounds. And I think about it in relation to those
languages and those arguments make sense. But for Ruby, they just
don't hold-up as well. Ruby's design just makes inheritance modeling
so easy to manage.

Seems to me, the term "MI" can be avoided --it can be called something
else, but the fact remains, and it is confirmed by implmentation after
implementation: MIXINS ARE MI. The only difference is that Mixins are
hobbled by an artifical class/module distiction.

T.
 
A

Austin Ziegler

While I agree that Ruby's modules/mixins can handle well many
situations where one would rely on MI eg in Eiffel and also that MI is
likely to unnecessarily increase the code's complexity to a degree of
unmaintainability, I can still remeber a (very) few situations when it
would have been handy. Eg with mixins, AFAIK you can't include class
methods and object methods at the same time, can you? Maybe I'm wrong
in this respect.

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.

When I started with Ruby, people weren't doing things that way and
weren't looking for ways to do it that way.

What people are doing is trying to get MI out of mixins, and they're not
that.

-austin
 
M

MenTaLguY

--=-57sNJi5F14xZguEJdQ9Z
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

Seems to me, the term "MI" can be avoided --it can be called something
else, but the fact remains, and it is confirmed by implmentation after
implementation: MIXINS ARE MI. The only difference is that Mixins are
hobbled by an artifical class/module distiction.

The inclusion of modules is also forcibly linearized[1] to avoid the
ambiguities which would otherwise be present with MI, which only works
because modules don't dictate the fundamental object representation like
classes do.

The class/module distinction is the only thing that lets us have any
kind of MI at all without severe ambiguities.

-mental

[1] i.e. the inheritance graph is made linear by ensuring that each
module only appears once in a class' ancestry

--=-57sNJi5F14xZguEJdQ9Z
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHB+A+SuZBmZzm14ERAgMmAJ9YhEKhNK1bd6E9n7z3LDNxTxCSQACfcllc
KtfqzBpLJF/gI9d5iBT3iaE=
=gWr3
-----END PGP SIGNATURE-----

--=-57sNJi5F14xZguEJdQ9Z--
 
M

MenTaLguY

--=-RISkpb2heQyltkigytWK
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

Yes. You have to think, when writing mixins, in the context of *all other= =20
mixins they will have to interact with*. Mmmmmm ... looks like MI problem= =20
to me.

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?

-mental

--=-RISkpb2heQyltkigytWK
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHB+E3SuZBmZzm14ERAgk/AJ9Pyurz8YD+jBm6sepkhiOa/Uc+tgCfemap
xuVesSiqBbqHv5AODzWAyvk=
=aeiv
-----END PGP SIGNATURE-----

--=-RISkpb2heQyltkigytWK--
 
M

MenTaLguY

--=-avIMBRwb5Yh8oTd3fM99
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

The bottom line being: if I was to *not* use inheritance for my purposes,= I=20
would have to reinvent the "inheritance wheel".

I did used to think that, by the way, but as I got more experience in OO
modeling, I found that wasn't generally true. Often using other kinds
of composition resulted in simpler and more maintainable/testable code.

(The one sticking point I would occasionally hit was delegation, as
languages like C++/Java don't have good support for it, but Ruby makes
it much easier than most.)

Would you be interested in working a simple example, to see if there are
other relationships besides "is-a" in your model?

-mental


--=-avIMBRwb5Yh8oTd3fM99
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHB+LWSuZBmZzm14ERAk5xAJ0ZeCMf3MBrrFt7z/tVYap8XWqLhQCggCsI
l6VVz4Lu5I7nidkRIU+oRhg=
=Bp6n
-----END PGP SIGNATURE-----

--=-avIMBRwb5Yh8oTd3fM99--
 
S

Sylvain Joyeux

[1] i.e. the inheritance graph is made linear by ensuring that each
module only appears once in a class' ancestry
True and false. You don't control *where* your module ends up in the
inheritance chain, something class inheritance partially does (i.e. you
have a tree or a graph, therefore a partial ordering). With modules, you
trade one problem with another.

Finally, you can't create module instances.

Sylvain
 
M

MenTaLguY

--=-1SOVA0JpVwZkQ8d00nud
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

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.

-mental


--=-1SOVA0JpVwZkQ8d00nud
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHB+RaSuZBmZzm14ERArpBAJ4zqSQz5PaqF3G7kh1UMRWgZk8exACfUzuC
+4Lai5uZ4Injtcb7OajI3ro=
=3Bn7
-----END PGP SIGNATURE-----

--=-1SOVA0JpVwZkQ8d00nud--
 
S

Sylvain Joyeux

(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.
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.

Well, hopefully I'll eventually be able to put the software in open source
and write some tutorials about it. We can restart the discussion then ;-)

Sylvain
 
S

Sylvain Joyeux

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.

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.

Sylvain
 
J

James Edward Gray II

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.

Delegation does not mean duck typing. You are confusing your
terminology.

James Edward Gray II
 
M

MenTaLguY

--=-GHvEvFfzz4+qbN9794mY
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

[1] i.e. the inheritance graph is made linear by ensuring that each
module only appears once in a class' ancestry
You don't control *where* your module ends up in the inheritance
chain, something class inheritance partially does (i.e. you have a
tree or a graph, therefore a partial ordering). With modules, you
trade one problem with another.

Linearization has nothing to do with modules per se. It's one
well-known technique for addressing ambiguity in the presence of
multiple inheritance. See for instance:

http://www.webcom.com/haahr/dylan/linearizations.html

The reason linearization is used is that partial ordering is not a
strong enough constraint to avoid ambiguity in method dispatching.
While linearization can be avoided, you would need to make other
trade-offs to compensate (i.e. you can't have your multiply inherited
cake and eat it too).

-mental

--=-GHvEvFfzz4+qbN9794mY
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHCANXSuZBmZzm14ERAn7yAJ9sa6NsP0wb/WPxKkOjNUtFpAUWWwCdE8U6
yYV51GvAvMTiVcksAQ1LBoY=
=Y7Tk
-----END PGP SIGNATURE-----

--=-GHvEvFfzz4+qbN9794mY--
 
M

MenTaLguY

--=-DIw1eAYIdg5lZLYDLhi5
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

=20
I think that programmers should have the right to do their own decisions.= =20
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=20
you-should-not-use-inheritance crap) and I have seen people using MI in=20
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

--=-DIw1eAYIdg5lZLYDLhi5
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQBHCAghSuZBmZzm14ERAqKJAKCSZtLliqEVOx7z0i+2VmFP1h1xbwCgv0Ck
ED1/tBsasSqKY8jV0TavxKM=
=Zicy
-----END PGP SIGNATURE-----

--=-DIw1eAYIdg5lZLYDLhi5--
 
A

Austin Ziegler

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.)

-austin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top