On multiple inheritance

Matthew Tuck matty@box.net.au
Mon, 07 Dec 1998 22:11:56 +1030


> Do you have a refence where I can read something about that? At first
> sight I can't see how that could solve the problem, given that you
> just have *two* ends but *multiple* inheritance.

I took a look.  The principle is that you can do MI in a closed-class
system given that you are willing to waste some space, e.g.

                        *
a:                      abc
b extends a:            abcdef
c extends a:            abc   ghi
d extends b,c:          abcdefghi

Whereas in two directional it's:
                        *
a:                      abc
b extends a:            abcdef
c extends a:         ghiabc
d extends b,c:       ghiabcdef

Algorithms can determine at compile-time an efficient layout, and
two-directional layout improves the wasted space.  In looking through it
it could only be of limited use in systems where classes are not fixed
at  compile time.

>> Note that if you take the position I advocate, of the separation of
>> specification inheritance and implementation inheritance, the
>> specifications are merged, but anything can happen with the
>> implementations.  You could take one, you could perform some sort of
>> merging of the two methods (e.g. in CLOS I think it is you can say
>> add the results of two functions), or you can override and specify
>> the behaviour directly.

> You mean you implement parts of a merged specification in one and
> other parts in another class?  How is that merging of specifications
> supposed to work?

No the implementation is merged.  The specification stays the same.

>> Note this is only a problem with "implicit" inheritance.  With
>> explicit inheritance you have to actually override all methods,
>> although you can use shorthands for direct inheritance.  I've only
>> heard of explicit inheritance being used with object-based
>> inheritance languages rather than the traditional class-based
>> inheritance langauges though.

> Sounds rather uninteresting to me.

Well it can be, and explicit inheritance isn't really a good idea I
don't think, but it shows the MI doesn't need to be a problem on the
language front.

> Nor do I. If I'd want such a behaviour, I wouldn't create two classes
> like this in the first place.

Yes, the book I'm currently reading advocates it and gives reasons which
I'm yet to look up.
 
>> I dislike coincidental compilation in all forms.  By this, I mean
>> the chance of passing the type system because the name matches
>> unintentionally.
> Yeah. Errors resulting from this may be hard to catch, because the
> "reasons" are scattered among more than one class.

That's true, it's always just struck me as unclean.

>> Method shadowing is potentially a slightly different form of
>> coincidental compilation.  The name could affect whether two methods
>> are merged.
 
> See my comment on merging, above.

I'm not sure what you meant by constructors merging in C++.  Anyway,
where you have repeated inheritance of Person into Employed Customer,
that's merging.

>> In this solution EmployedCustomer is not a subtype of Customer so
>> you can't ask for a customer, you must ask for a person.  This is a
>> real loss to the type system, and is the sort of thing inheritance
>> was made for.
> You would have to ask for Person.c. Then you'd get a customer. But, as
> I said, I wouldn't like this solution anyway.

Ok, I can see how that would work.  But two things strike me about this
irrespective of cleanliness, (a) it's just an implementation of
inheritance which would require explicit inheritance and (b) you've
created your own EmployedCustomer method which uses method combination,
which can be supplied at the language level.

> > In C++ it will complain at compile-time of the user of the template,
> > rather than being able to complain when you compile the template.

> You mean if I tried to instantiate the template with a parameter where
> some required member of Person were not present, it would complain of
> the use of that (now unknown) member?The error message would not be
> very enlightening, and I must consider this kind of "type checking" to
> be rather unsatisfactory.

Since you can't specify any bounded generics (must be derived from A,
where A /= object) it let's you refer to a method, which may or may not
be there.  At least the last time I looked at C++ templates, which was a
little while back.  There's no good specification of what the template
provides that can be checked on both sides.  But C++ is the exception in
languages.

>> There's no subtyping here either, which is why I don't like this.
> Please explain. I don't understand what you mean.  class Employed (class Person pE) {};

Ok, I'm getting a little confused.  Would you be able to give a full
example of a Person, Customer, Employed and Employed Customers?  Then I
might be able to comment a bit further.

> > This seems to me to be getting back to the exact same system of rank
> > with multiple inheritance, except you don't have subtyping.
> But it's more obvious to the user, and because of normal inheritance
> rules you got an "override" relationship instead of name clashes.

I'm trying to put my finger on exactly what I don't like about it.  I
think it's partly that you were saying you have Employed Customer
Persons and Customer Employed Persons.  These might be valuable concepts
at the implementation level but at the specification level you want just
one to avoid complicating things.  Could you assign one to the other for
example?

>> I also think solutions to multiple inheritance come a lot clearer
>> when you separate specification from implementation.  In this case,
>> things work differently depending on which you're talking about.  Its
>> a very powerful idea.

> I feel we might have to discuss this "separate specification from
> implementation" issue in more detail. Up to now I viewed a
> specification as something roughly equivalent to a class consisting
> entirely of (usually pure) virtual functions in C++, maybe enriched by
> assertion and the like (if and when we choose to implement such a
> thing, which I strongly advocate). If there is more to it, I'd be
> really like to know about it.

Well it depends exactly what you want to do.  That's basically one way
of looking at it.  When you start looking at things that way though, you
see things in a different light.  For example - multiple
implementations, separation of code inheritance from specification
inheritance, etc.

One thing I just thought of which I'd been trying to think of a solution
to before - inherited assertions (precons, postcons, invariants) go in
the specification, while implementation specific assertions go in the
implementation.

It's true that most of this sort of thing can be done using subclasses,
but enforcing it allows increased flexibility since it enforces the
regime upon you.  From a logical point of view they are separate after
all.  An intelligent editors can help with stuff like creating the
template of a new implementation, for those worrying about extra typing.

> All I want to say is that in this case I stumbled upon MI as a concept
> found in common programming languages which I didn't feel comfortable
> with, for the reasons I already gave. But I recognize its general
> desirability. So I looked for other concepts which would make MI's
> functionality available, but without the hassle.

OK, not a problem.  Aggressive attacking of concepts is the only way I
know to arrive at the truth.  =)  The most serious problem I see with MI
is its implementation.  That's the part I know the least about and we
need to find out about it.  At the langauge level I think your concerns
can be addressed.

-- 
     Matthew Tuck - Software Developer & All-Round Nice Guy
                              ***
       Check out the Ultra programming language project!
              http://www.box.net.au/~matty/ultra/