Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sun-barr!apple!agate!shelby!neon!craig From: craig@Neon.Stanford.EDU (Craig D. Chambers) Newsgroups: comp.object Subject: Re: Examples of Multiple Inheritance? Keywords: multiple inheritance Message-ID: <1990Dec13.035418.28040@Neon.Stanford.EDU> Date: 13 Dec 90 03:54:18 GMT References: <980@culhua.prg.ox.ac.uk> <841@echbull.bull.fr> <18090@neptune.inf.ethz.ch> Organization: Stanford University Lines: 87 In article <18090@neptune.inf.ethz.ch> brandis@inf.ethz.ch (Marc Brandis) writes: >First, there is no really clean way to solve the naming and consistency >conflicts that arise when using multiple inheritance. I do not want to list >them here as I think you know them. If one will be found, I have no major >objection against multiple inheritance any more. However, I am not sure >whether it will ever be found. In prototype-based languages, the programmer explicitly constructs an initial (prototypical) instance of each type to go along with a shared behavior object (thus splitting a single class declaration into two cooperating object declarations). All issues about resolving instance variable name clashes are largely moot, since the programmer is already explicitly handling the representation of instances anyway. See the "Organizing Programs without Classes" paper that I've mentioned before (available in postscript form via anonymous ftp from otis.stanford.edu) for a longer discussion. Name clashes among inherited methods are still an issue, of course. >As long as these conflicts are not resolved, I prefer to see in my code >what is going on instead of having to guess what the compiler will do. I >know that I may have to write a little more using simple inheritance and >delegation, but everything is under complete control of the programmer. > >[reordering....] > >Third, as multiple inheritance is a rather powerful construct, which - at >least at the moment - requires a lot of rules how conflicts are resolved, >I would assume that lots of programmers may not understand all these rules >well, so that they may introduce more errors than necessary. I agree that MI rules should be simple enough for programmers to use effectively. I think this requires that the language only resolve *obvious* ambiguities automatically (i.e. silently), such as between a child and its parent; any other ambiguities may be reasonably forwarded to the programmer for explicit resolution. Note that this does not necessarily mean that each name clash must be resolved separately by writing code to handle each name clash; fancier schemes and/or environment tools would allow a more declarative way to resolve whole groups of name clashes in a standard way (such as the mixin combination rules supported in Flavors and CLOS). I believe the programmer should be informed about these potential conflicts, make sure that they are of the "standard variety," and hit a button to have the system automatically do the necessary programming to resolve the ambiguity. Hopefully this approach would lead to more reliable use of MI without sacrificing the ease of programming that is the hallmark of the more automatic, complex, error-prone approaches. >Second, multiple inheritance is a rather expensive construct (compared to >single inheritance). In C++, you even have to pay some overhead when you >are just using single inheritance because multiple inheritance has been added. >I do not think that the few places where I could use multiple inheritance >instead of some kind of delegation are worth this overhead. As others have said, this is an implementation issue, not a language issue. The Self implementation includes MI at virtually no cost, since many performance-critical messages are statically bound at compile-time anyway, therefore leading to zero run-time lookup overhead, and all the rest of the run-time message sends use both in-line caching (pioneered by the Deutsch-Schiffman Smalltalk-80 implementation (i.e. ParcPlace Smalltalk)) and global lookup caching. So run-time lookups only happen if none of the caches contain an entry for the message being sent. These latter two run-time caching techniques are not quite as fast as the standard indirect procedure call implementation of pre-MI C++, but are faster than the same scheme extended to handle dynamically-typed languages with the possibility of lookup errors (which for all intents and purposes includes Eiffel, since Eiffel's static checking doesn't exclude run-time lookup errors). Also, techniques for handling MI in statically-typed languages using a single level of indirection are appearing in the literature as well [see OOPSLA'89 and PLDI'90 for some papers]. If you're really concerned about message send speed, you should be trying to get OO language implementors to include techniques that support more static binding of messages (such as customization, type analysis, and splitting as in the Self compiler). And for those messages that remain dynamically-bound, you could probably afford a little extra compile time (consider it part of the time to optimize programs) to do fancy things to support single-level-of-indirection calls. If you're concerned about space overheads of MI, other implementations of MI than the one in C++ 2.0+ have low space overheads, comparable to pre-MI C++ implementations (i.e. a word or two extra per object). -- Craig Chambers