[Warning ... I'm afraid I got a bit long winded ... sorry]
I'd appreciate it if someone could clarify once and for all
how the diamond inheritance problem doesn't exist in Ruby.
First, some clarification: What is the problem with diamond inheritance?
Diamond inheritance is a pattern of MI that has a parent class (or module in
Ruby's case) that is reachable via different inheritance paths. The specific
term for this is Repeated Inheritance.
The basic problem with Repeated Inheritance is how instance variables defined
in the repeated class should be handled. Should there be one copy total, or
one copy for each repeated parent class.
(An aside: Another thread on this topic indicated that method resolution was
a problem in repeated inheritance. That is incorrect, it is an issue in any
form of multiple inheritance (even the module variety), and does not specific
to repeated inheritance).
Ok, now that we've clarified the question, we can answer it: Why isn't
repeated inheritance a problem with modules?
Some people will say its because modules don't define state. Although
correct, its misleading because it implies that classes do define state.
Classes (in Ruby) don't define state either.
Wait a minute, let me say that in a better way!
Module (and Classes) in Ruby don't contain any /instance/ state. The instance
variables in a ruby object are managed by the instance itself, not the
classes. This is different from most other OO languages.
Most other languages think of objects as little areas of memory. Consider a
class B that inherits from A. Its memory layout would look something like
this:
><--- A --->|
><------------- B ------>|
The memory for the A part of the object takes up the first section of memory
of a B object. In fact, someone looking at the beginning of the B object
would see a real, live A object in memory. That's how polymorphism works in
languages like C++.
When you have repeated inheritance, the whole memory layout scheme gets all
screwed up. Lets introduce two new classes, C which also inherits from A,
and D which multiply inherits from B and C. What does the memory layout of D
look like?
><--- A --->| |<--- A --->|
><------------- B ------>|<------------- C -------->|
><------------------------------------------------------ D --->|
Yep, two copies of A. One for B and one for C. And that's the problem. Most
times you only want one copy of A.
Different languages handle the repeated inheritance differently. C++
introduces "virtual" inheritance, where the base class is not located in the
same memory area, but is (possibly) displaced. Java only allows MI in
interfaces (and since interfaces have no data memory layout, the problem
doesn't arise).
Eiffel has a flexible approach to repeated inheritance. Any feature (Eiffel's
term for methods and instance variables) that ends up with the same name in
the final class, gets only one copy of that feature. You can rename features
as you inherit, so if a feature ends up with two names in the final class,
you get two copies of it, each with its own name (so there is no ambiguity
either ... very clever). Eiffel's solution is very clean for the developer,
but really hard to implement correctly.
Getting back to Ruby ... The whole memory layout problem is brought about by
thinking of variables as shoeboxes which hold values. Once you have
shoeboxes, you got to pay attention to how you arrange them.
Ruby objects don't have a memory layout for the instance variables. In fact,
it is quite possible that two objects of the same class don't even have the
same instance variables.
So, in summary, why is diamond (repeated) inheritance not a problem in Ruby
modules?
Because the memory layout issues that force multiple copies of a base class
just don't exist in Ruby.
···
On Thursday 28 July 2005 10:00 pm, Daniel Brockman wrote:
--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)