🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: comp.lang.eiffel
1 messages
1 total messages Started by bertrand@hub.ucs Thu, 12 Jan 1989 06:33
Re: multiple inheritance example
#41
Author: bertrand@hub.ucs
Date: Thu, 12 Jan 1989 06:33
205 lines
9129 bytes

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
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads