Xref: utzoo comp.lang.eiffel:1355 comp.object:2477 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sun-barr!decwrl!shelby!neon!craig From: craig@Neon.Stanford.EDU (Craig D. Chambers) Newsgroups: comp.lang.eiffel,comp.object Subject: Re: Inheritance and Information Hiding Message-ID: <1991Feb1.015749.10111@Neon.Stanford.EDU> Date: 1 Feb 91 01:57:49 GMT References: <10612@pasteur.Berkeley.EDU> <485@eiffel.UUCP> <488@eiffel.UUCP> Organization: Stanford University Lines: 105 In article <488@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes: > > Will anyone take up the simplest example (discussed in my >posting <485@eiffel.UUCP>): a class POLYGON exists and has >a procedure ``add_vertex'', which it exports. >You want to add a class RECTANGLE. What do you do? > >3. Accept that RECTANGLE inherits from POLYGON and does not export >``add_vertex''. (This procedure violates the invariant of class >RECTANGLE anyway, so the consistency rules forbid it to be exported, >but of course non-exported routines do not have to maintain the >invariant.) To avoid burdening the compiler writer, >call this ``implementation inheritance'' and disallow >polymorphic assignments of the form p := r where p is of type >POLYGON and r of type RECTANGLE. > >4. Accept that RECTANGLE inherits from POLYGON and does not export >``add_vertex''. Permit polymorphic assignments of the above form; >but have the type checker reject as invalid any system which would >contain both such an assignment and a call p.add_vertex (...), since >it may lead to a run-time inconsistency. Do not introduce >any distinction between ``implementation inheritance'' and any other >kind. > >Based on not inconsiderable experience with the design of O-O systems >and class hierarchies, I believe that solution 4 is the only reasonable >one. I would be interested to see any technical counter-argument, >and alternative solutions to the problem mentioned above. > >Of course solution 4 causes more work for implementers, since >type checking must now be done on an entire system rather than a >single class. But that's what computers are for: tedious, exhaustive >work, especially when (as here) the consequences of not doing that >work are potentially rather damaging. In earlier articles I have >described a strategy for doing the work in an incremental fashion >so that its performance overhead is acceptable. This issue has bothered me for a while, but I've not posted about it before. Now with this challenge, I can't resist. Option 3 seems like the only reasonable solution to me. And I'm not arguing from an implementor's point of view; I'm not averse to a complex implementation if it supports a nice programming model and environment. The real problem with option 4 is that a large existing legal program can be invalidated by a single added line. Say you have a large application manipulating various shapes, including rectangles and polygons as above. Now say someone adds a single line to the program, say an assignment "p := r". If in the rest of the application there are no such assignments, but many calls on p.add_vertex, then all these calls are suddenly invalidated by the new assignment. This seems incredibly wrong. The legality of earlier add_vertex calls shouldn't depend on whether some future programmer makes an assignment "p := r"; instead, the new assignment should be marked illegal. This exactly corresponds to option 3: making rectangle inherit code from polygon, but not making rectangle a subtype of polygon. Consider the opposite scenario: In a large application, there are countless assignments of the form "p := r", but no calls of p.add_vertex. The environment could allow this to happen, and make rectangle a legal subtype of polygon, by pretending that the add_vertex feature didn't exist (after all, the application didn't need it). Then some programmer adds a call to p.add_vertex. Again, this last call should be marked in error, since earlier treatment of rectangles as subtypes of polygons has disallowed any future use of add_vertex. This approach is a variant of option 3, with the extension that unused features can be ignored when forcing one class to be a supertype of another. This feature is extra-lingual (part of the environment), but may be nice to have around to support reuse of parts of existing code (e.g. the rectangle class is reusing (inheriting from) part of the polygon class by pretending that certain unneeded operations like add_vertex aren't there). So I conclude, on the grounds of practical programming concerns and usability of the resulting language, that option 3 is far preferable to option 4. Actually, option 6 proposed by Barry Margolin (changing the class of a rectangle to a polygon after an add_vertex) may be a useful alternative in some situations. A more general, cleaner facility for changing the implementation of an object at run-time is called dynamic inheritance in Self. Use of this feature would require distinguishing interface from representation/implementation, with rectangle being a particular representation of polygon but not a distinct type. Then after the add_vertex, the representation of a polygon that was a rectangle changes, but not its type (it's still a polygon). None of these options works nicely in all conceivable situations, but instead imposes certain restrictions on use (limits what the programmer can write). I argue that the restrictions imposed by option 3 (assignments require subtype compatibility) and/or option 6 (some subclasses are not separate types) are more reasonable and likely to be acceptable in practice than those for option 4 (existing programs may be invalidated by new code). However, if the programmer needs to manipulate both rectangles and polygons and assign between them, do add_vertex's on polygons, and treat rectangles as a distinct type (have additional behavior for rectangles), then none of these static solutions works. The remaining solution is to perform dynamic type-checking (or, equivalently, conditional assignment in Eiffel) on calls of p.add_vertex to verify that p doesn't contain a rectangle, or to make a version of add_vertex for rectangles that signals a run-time error. -- Craig Chambers