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