[comp.lang.eiffel] multiple inheritance example

snyder@hplabsz.HPL.HP.COM (Alan Snyder) (01/12/89)

I am posting the enclosed letter at the request of Bertrand Meyer.
We believe it raises issues of general interest.
================================================================================
To: meyer
Subject: a question
Reply-to: snyder@hplabs.hp.com
Date: Fri, 30 Dec 88 15:29:06 PST
From: Alan Snyder <snyder@hplsny>

Bertrand,

I have a question about the example you used in your column on multiple
inheritance in the Nov/Dec issue of JOOP.  To refresh your memory, you gave
an example of walking menus, which included a class SUBMENU that inherits
from both MENU and ENTRY.

It is clear to me why a SUBMENU must be an ENTRY: it must be an ENTRY
because it is an entry in a menu.  However, it is not clear to me why a
SUBMENU must also be a MENU.  As an alternative, I would consider having a
SUBMENU contain a MENU as an attribute; the execute action would have the
effect of bringing up that contained MENU.  I justify this alternative by
noting that the MENU operations are not needed on the object that lives in
the enclosing menu (the menu entry) and claiming that the two behaviors
need not be represented by a single object.

My question: What is your reasoning in favor of the multiple inheritance
solution over this alternative?  Do you see one alternative as strongly
superior to the other?  On what basis do you choose between these
alternatives?

Thank you.

  Alan

bertrand@hub.ucsb.edu (Bertrand Meyer) (01/12/89)

This is a copy of my answer to <2791@hplabsz.HPL.HP.COM>
from snyder@hplabsz.HPL.HP.COM (Alan Snyder).
Both that article and my answer were mail messages initially, but they
should be of interest to readers of this newsgroup.
> 
> I have a question about the example you used in your column on multiple
> inheritance in the Nov/Dec issue of JOOP.  To refresh your memory, you gave
> an example of walking menus, which included a class SUBMENU that inherits
> from both MENU and ENTRY.
> 
> It is clear to me why a SUBMENU must be an ENTRY: it must be an ENTRY
> because it is an entry in a menu.  However, it is not clear to me why a
> SUBMENU must also be a MENU.  As an alternative, I would consider having a
> SUBMENU contain a MENU as an attribute.
>
> [...]
>
> What is your reasoning in favor of the multiple inheritance
> solution over this alternative?  Do you see one alternative as strongly
> superior to the other?  On what basis do you choose between these
> alternatives?

	The arguments below are not as precise and formal as I
would like them to be. We have developed a certain style of
using multiple inheritance, that we and other Eiffel users have
applied successfully to the development of many lines of code,
and I feel quite strongly about it because it works so nicely
in practice. Things simply seem to fit well this way.
On the other hand I can't really say
I have a full-fledged theory to support it. Answering your
comments might help improve the theory.

> the MENU operations are not needed on the object that lives in
> the enclosing menu (the menu entry) and [you] claim that the
> two behaviors need not be represented by a single object.

	It seems to me that a submenu should have the behavior of
a menu, or at least part of it. For example it itself contains
further subentries. It should have a procedure to add such an
entry, remove it so on. Class MENU has procedures ``display'',
``erase'' etc. all of which seem meaningful for submenus.

(By the way, some of these were not exported in the submenu
class of the current Eiffel Graphics Library, called
MENU_ENTRY. I was quite embarrassed when I realized that.
However I take this to be simply a mistake which will be
corrected in the forthcoming version.)

> As an alternative, [you] would consider having a
> SUBMENU contain a MENU as an attribute; the execute action
> would have the effect of bringing up that contained MENU.
> (...) What was [my] reasoning in favor of the multiple
> inheritance solution over this alternative?
> Do you see one alternative as strongly
> superior to the other?  On what basis do you choose
> between these alternatives?


	I wish I had perfect answers. My general guideline is
clear: I see inheritance as the ``is'' relation and client as
``has''. These are natural language terms and not perfectly
rigorous. I am aware in particular that our form of inheritance
(where a descendant can hide a feature exported by an ancestor
or conversely) may appear to cast some doubts as to what ``is''
really means. However the following points are worth
considering.

1. Cases in which inheritance is definitely NOT appropriate are
usually pretty obvious. The example from the Trellis-Owl people
that I quoted in my column is typical: having PLANE inherit
from COCKPIT (or was it HOUSE from DOOR). In general, when
``has'' applies but not ``is'' (and hence inheritance is
improper), any doubt is easily removed.

2. The more difficult case is when ``is'' apparently applies
but you are not quite sure (as in the submenu case). However it
seems to me
that in a sense you can always interpret ``is''
as ``has'' and thus do away with inheritance altogether.
(Again I am aware of the limitations of using
such natural-language-based criteria, but we are in the domain
of information modeling here and I don't see how to avoid human
interpretation.)
If ``every A may also be viewed as a B'' (the basis for applying
``is'', or inheritance), then you can always reinterpret this
by saying ``every A has a part of it which is a B''. In other
words, A could be rewritten as a client of B. It seems to me
that if you take your favorite example of unquestionable
inheritance (say polygons and rectangles, or anything else) you
can rewrite it in such a way that it uses the client relation
instead (i.e. each rectangle will have an attribute that refers
to an object describing its polygon features).
[If I understand figure 1 in your paper in the Shriver & Wegner
volume, page 170, then that's what it means.]
Of course you may then lose subtyping: polymorphism, dynamic
binding and so on (see point 4).

3. If a submenu is not a menu, then you cannot (in the SUBMENU
class) write something like ``entry (i)'' to refer to the i-th
entry, but you must write ``associated_menu.entry (i)''. This
will be the same for all menu features of submenus. This is
only a matter of convenience, but it reinforces the previous
point. In a RECTANGLE class, being able to write ``rotate (30)''
rather than ``associated_polygon.rotate (30)'' could also be
dismissed as a matter of convenience.

4. This leaves the need to define more precisely what ``is''
means. ``Every operation applicable to instances of A must be
applicable to instances of B'' seems to provide the
appropriate definition.
This means that subtyping and inheritance are merged.
This is basically the approach taken in
Eiffel and I believe it applies to menus and submenus.

5. However if taken literally the previous definition excludes
possibility of
a descendant hiding (in its own interface specification) some
of the features that were exported by ancestors.
There are some arguments in favor of this approach. However it
was not retained in Eiffel for various reasons, theoretical and
practical. Among the practical ones is that even simple
examples seem to require hiding features that ancestors
exported. For instance a full POLYGON class will probably have
a procedure ``add_vertex''; this should not be exported by
RECTANGLE. You could always avoid the problem by complicating
the inheritance hierarchies (having a class POLYGON which
does not include ``add_vertex'', with EXTENDIBLE_POLYGON
and FIXED_POLYGON both being heirs of this class,
the former introducing ``add_vertex'', and RECTANGLE
inheriting from FIXED_POLYGON). However this seems much too
restrictive in practice.

This ability not to export previously exported routines means
that exceptions are accepted to the general rule that inheritance
includes subtyping. What we are trying to do, however, is to
avoid being needlessly restrictive here. For example in the
above case I would like to avoid rejecting offhand an
assignment such as p := r just because one feature exported
by POLYGON is not exported by RECTANGLE. The rejection should
only occur if the system contains an actual call to such a
feature, e.g. p.add_vertex. (Flow analysis should not be taken
into consideration.) Although our current compiler does not
handle such checks (this is of course very similar to the
problems discussed by William Cook in his paper), we are
confident that we can fill this hole soon.

This leaves a definition of inheritance that is not as
clear-cut as the one above (see 4.). ``Every'' may have to be
replaced by ``most''.

6. An associated criterion is the following: an instance of B
is acceptable in any list (tree, queue, stack etc.) of
instances of A. In the example at hand, if you have a list of
all menus in your application, it should include submenus as
well.

7. I suspect giving submenus an attribute of type MENU would be
even more cumbersome than suggested above. In particular we
will need to record the information that such menus are special
in some way; at the least, we want to associate with them a
reference to the entry from which they come in the parent menu,
and this parent menu itself. So we need attributes of the form

	seed_entry: ENTRY
	parent_menu: MENU

Since these do not make sense for general menus, we will need a
special class SUBMENU anyway, inheriting from MENU.
Now entries which correspond to submenus are also special and
(this is the part that you considered as self-evident) should
be described by a class, say MENU_ENTRY, which inherits from
ENTRY and has the attribute

	associated_menu: SUBMENU

Furthermore some routines of MENU will need to be redefined for
SUBMENU. For example you may want to display submenus in a
special way (shifted both vertically and horizontally from the
parent menu), so procedure ``display'' (as well as ``erase''
etc.) will be redefined in SUBMENU.

But there is a close correspondence between instances of
SUBMENU and MENU_ENTRY; apparently a one-to-one correspondence.
So we need invariants of the form

in class MENU_ENTRY

	not associated_menu.Void
		and then associated_menu.seed_entry = Current

in class SUBMENU

	not seed_entry.Void
		and then seed_entry.associated_menu = Current

This brings in a considerable amount of complication which
appears totally unwarranted. All this simply vanishes if you
accept that a ``menu entry'', or submenu, is both a menu and an
entry.


-- Bertrand Meyer
bertrand@eiffel.com

donw@cognos.uucp (Don Walker) (01/17/89)

In article <1101@hub.ucsb.edu> bertrand@hub.ucsb.edu (Bertrand Meyer) writes:
...
>	I wish I had perfect answers. My general guideline is
>clear: I see inheritance as the ``is'' relation and client as
>``has''. These are natural language terms and not perfectly
>rigorous. I am aware in particular that our form of inheritance
>(where a descendant can hide a feature exported by an ancestor
>or conversely) may appear to cast some doubts as to what ``is''
>really means. However the following points are worth
>considering.
...
>2. The more difficult case is when ``is'' apparently applies
>but you are not quite sure (as in the submenu case). However it
>seems to me
>that in a sense you can always interpret ``is''
>as ``has'' and thus do away with inheritance altogether.
>(Again I am aware of the limitations of using
>such natural-language-based criteria, but we are in the domain
>of information modeling here and I don't see how to avoid human
>interpretation.)
>If ``every A may also be viewed as a B'' (the basis for applying
>``is'', or inheritance), then you can always reinterpret this
>by saying ``every A has a part of it which is a B''. In other
>words, A could be rewritten as a client of B. It seems to me
>that if you take your favorite example of unquestionable
>inheritance (say polygons and rectangles, or anything else) you
>can rewrite it in such a way that it uses the client relation
>instead (i.e. each rectangle will have an attribute that refers
>to an object describing its polygon features).
...

One of the major differences between inheriting a class and being a client of a
class is that being a client restricts you to using the public interface.  If
you inherit you have access to all the features, public and private, of your
ancestor.  There is nothing wrong with this in the cases where you really need
it.  However, if the choice is between inheriting and being a client then the
current inheritance mechanism is overkill since you only required access to the
public features.  This violates the principle of information hiding discussed
in chapter 2 of The Book.  At our company we are working on a large system with
an expectation of over 1000 classes.  Any time we inherit instead of become a
client we are increasing the dependencies in the inheritance trees, which are
becoming very complex.

It is my opinion that it would be useful to be able to distinguish between
unrestricted inheritance (the current implementation) and limited inheritance
which would allow only the public interface to be "inherited".  Since your
other arguments for using inheritance versus becoming a client of a class made
sense to me I feel that what I think is a relatively minor language extension
would remove a major objection to the use of inheritance.

I am fairly new to both Eiffel and OOP and this suggestion is only my opinion.
I'd be interested in hearing what you and others think of it.  I have been
working on largish software products for a long time and know from bitter
experience how hard things get when your dependencies get too large.  I believe
this extension to the langauge would allow a useful technique to be retained
while keeping the dependency problem down to one of primarily the public
interface, which may be hard enough to manage itself.

I'd just like to add that I appreciate your participation in this newsgroup.
Its like being in at the start of C and being able to talk to Kernighan and
Ritchie.  I hope Eiffel is just as successful.
-- 
Don Walker               Cognos Incorporated     S-mail: P.O. Box 9707
Voice: (613) 738-1338 ext 5934                           3755 Riverside Drive 
  FAX: (613) 738-0002                                    Ottawa, Ontario 
 uucp: uunet!mitel!sce!cognos!donw                       CANADA  K1G 3Z4

gore@eecs.nwu.edu (Jacob Gore) (01/22/89)

/ comp.lang.eiffel / donw@cognos.uucp (Don Walker) / Jan 16, 1989 /
>One of the major differences between inheriting a class and being a client of
>a class is that being a client restricts you to using the public interface.
>... the current inheritance mechanism is overkill [if] you only required
>access to the public features.
>It is my opinion that it would be useful to be able to distinguish between
>unrestricted inheritance (the current implementation) and limited inheritance
>which would allow only the public interface to be "inherited".

The issue of whether an heir should get access to the internal workings of
its ancestor seems to split people into two camps.  Suppose class B
inherits from class A.  Should the implementor of B be able to couple B's
implementation so tightly to A's implementation?  The problem, of course,
is that if the implementor of A decides to change the implementation, B is
likely to break (unless it keeps using the older version of A -- but having
to maintain multiple implementations of the same class is not a pleasant
thought).

In one camp you have people who say, "No, this coupling is too tight".
(For example, Barbara Liskov made this point in her keynote address at
OOPSLA'87.)  In the other, you have people who say that the implementor of
B should be allowed to choose whether or not to risk the tight coupling.

It is in the interest of the provider of a class (implementor of A) to hide
the implementation, but it is in the interest of the user of that class
(implementor of B) to be able to take advantage of it.  Conflict of
interests!

I think the idea that the implementor of B is the one taking a risk is too
simplistic: if the software system is scheduled to go into QA in a week,
and A's implementation is changed, thus breaking B, it will be the
implementor of A who will be pressured (or forced) to change the
implementation back so that B can keep functioning.  This is why I tend to
lean to the "this is too tight" side.

Don Walker suggests that both types of inheritance should be available.  I
agree with the general idea, but it still begs the question:

	Who decides which type to use: the author of the inherited
	class, or the author of the inheriting class?

Jacob Gore				Gore@EECS.NWU.Edu
Northwestern Univ., EECS Dept.		{oddjob,gargoyle,att}!nucsrl!gore