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?
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’?
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
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.
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.
Should A’s methods or B’s methods break when called on an instance of
C?
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.
I’m probably just being naive here, but what problems does MI solve
that mixins can’t?
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:
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.
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
class LinuxVideoPlayerApplication < LinuxApplication
To make this issue a bit more concrete, is there ever a time when you’ll
have more than one of these objects instantiated in memory and, for
example,
a list of Application references where some point to LinuxApplications,
some
point to LinuxVideoPlayerApplications, etc.? I suspect you’ll only ever
have
a LinuxVideoPlayerApplication. In that case, inheritance doesn’t solve
any
problems. The mixin (module) approach lets you refer to the object
generically as an Application when that makes sense, a Video Player when
that makes sense, etc. without inheritance.
Also, I suspect this hierarchy violates the Liskov Substitution
Principle,
which is our most effective definition of what inheritance really means
in
software. Is a LinuxVideoPlayerApplication object substitutable
everywhere a
LinuxApplication is used? Or, does the former “specialize” the behavior
of
the latter in such a way that substitution would break the code
expecting a
LinuxApplication? This is very common in “deep” class hierarchies. The
idea
that subclasses “specialize” the behavior of superclasses has to
considered
carefully.
Should A’s methods or B’s methods break when called on an instance of
C?
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.
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
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.
On Sat, 2007-10-06 at 08:47 +0200, Sylvain J. wrote:
Yes. You have to think, when writing mixins, in the context of all other
mixins they will have to interact with. Mmmmmm … looks like MI problem
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?
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”.
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?
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
[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.
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.
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
On Sat, 2007-10-06 at 08:47 +0200, Sylvain J. wrote:
Yes. You have to think, when writing mixins, in the context of all
other mixins they will have to interact with. Mmmmmm … looks like
MI problem 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?
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.
On Sun, 2007-10-07 at 04:34 +0900, Sylvain J. wrote:
[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:
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).
On Sat, 2007-10-06 at 23:33 +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.)