Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!rutgers!ucsd!ucbvax!pasteur!icsib1.Berkeley.EDU!hws From: hws@icsib1.Berkeley.EDU (Heinz Schmidt) Newsgroups: comp.object Subject: Re: Subclassing vs. subtyping Message-ID: <18129@pasteur.Berkeley.EDU> Date: 9 Oct 89 20:24:14 GMT References: <33009@cornell.UUCP> Sender: news@pasteur.Berkeley.EDU Reply-To: hws@icsib1.Berkeley.EDU (Heinz Schmidt) Distribution: comp Organization: International Computer Science Institute, Berkeley Lines: 132 In article <33009@cornell.UUCP>, wilk@svax.cs.cornell.edu (Michael Wilk) writes: > Several times at OOPSLA I heard references to the issue of > "subtyping" versus "subclassing." This issue ties in with the > concept of inheritance and the concept of classes as sets. Below > is my understanding of this issue based on what I heard at the > conference. I do not claim to be an authority, so please correct > me where I am wrong. ... > One way to resolve this is to say that RECTANGLEs are not the > quadrilaterals we normally think of, but include any extensions > so long as its methods are supported in the subclasses. This is the point taken in "subtype = subset" approaches, like Cardelli: A semantics for multiple inheritance, inf. & comp. 76, 1988 Goguen,Meseguer: Order-sorted algebra slolves the constructor-selector, multiple representation and coercion problem, 1987 IEEE proceedings of ??? Smolka, Nutt: order-sorted equational computation, SR 87-14, Univ. Kaiserslautern, 1987 These semantics do not only explain coercion RECTANGLE -------- HEIGHT ---> INT A A * | | | | VENETIAN_BLIND --- HEIGHT ------+ which allows to "implement" VENETIAN_BLIND height in terms of RECTANGLE height, but also extension like the follwowing (I assume two more RECTANGLE instance variables, X, and Y, defining the location): RECTANGLE -------- MOVE ---> RECTANGLE A A | * | * | | ** | | V VENETIAN_BLIND --- MOVE ---> VENETIAN_BLIND The obvious point in the above set interpretation is: a VENETIAN_BLIND is a RECTANGLE, hence we can apply MOVE. (**) additionaly explains the intuitively clear point that a MOVE of a VENETIAN_BLIND results in a VENETIAN_BLIND at a different location, and the diagram also sheds some light on the relation between the different objects and MOVE methods involved. The subset interpretation implies: (1) a VENETIAN_BLIND *IS* a RECTANGLE -- without forgetting any of its instance variable values in (*), (2) the RECTANGLE move operation -- presumable just changing the values of X and Y, applies to all RECTANGLES, including VENETIAN_BLINDs, (3) hence the VENETIAN_BLIND move is the restriction of the RECTANGLE move to VENETIAN_BLINDs, or (4) in other words, MOVE on RECTANGLE was MOVE for *ALL* rectangles including VBs in the first place (because it was expressed in terms of methods supported by all rectangles). Obviously, inheritance is more than coercion: it does something to the inherited methods involved: VENETIAN_BLIND MOVE(X) == **(RECTANGLE MOVE (* (X))). It transforms this method to one that can be consistently applied to VENETIAN_BLINDs. On the implementation level (code sharing), this transformation is void, since the involved mappings (*,**) are identies -- provided the involved objects are of the proper type. > But there's nothing to insist that a programmer adopt this > regimen; subclassing simply doesn't enforce it. What is more, > RECTANGULAR-THING cannot generate instances of every object in > the set of objects it represents; it can generate a STRICT- > RECTANGLE but not a VENETIAN-BLIND. I believe C++ deals with > this by calling RECTANGULAR-THING an "abstract" class and > forbidding it from generating any instances at all. This is an > understandable but unsatisfactory solution. In the above setting, the constructors of RECTANGLEs (like NEW_RECTANGLE or what has your favorite OO language) are all constructors of the set of RECTANGLE subtypes including RECTANGLE itself. In fact each class with a constructor has both aspects: the set of all instances created by the respective constructor (I'd prefer the term "essential rectangles") and the unions of all the subtypes (here, say, "abstract rectangles"). > ... > What happened to inheritance? It was useless! Since SQUARE is > more restricted than RECTANGLE we need *fewer* instance > variables, not *more* as subclassing allows. And the HEIGHTEN > and WIDEN methods that applied to RECTANGLEs make sense only if > we allow squares to be automatically transformed into rectangles. The choice of instance variables partly is a matter of taste. I believe at the heart of OO programming is the understanding that instance variables, in general, are *not* independent. In general a method will operate on a couple of variables. So I disagree. Are you saying a square doesn't have a width? Or that it doesn't have a height? It has both of them, whatever you choose to represent them. And when I ask my little son whether he can widen and heighten a square, the answer is a clear "yes". In the above setting you might say, a SQUARE is a rectangle with the constraint HEIGHT=WIDTH (some OO languages allow to express such constraints). Again the "subtype = subset" diagram above helps me understand the relation between the involved WIDEN methods. SQUARE WIDEN must preserve the constraint, because it must return a SQUARE. You might say, there is no obvious way to extend the RECTANGLE WIDEN method such that it satisfies this requirement. Well, there's redefinition beside inheritance. In some OO languages you could even reuse the RECTANGLE WIDEN method and hook onto it a "after WIDEN" method that adjusts the Y instance variable upon a call to WIDEN. In other words you tell the compiler more details about (**) and the compiler produces an efficient "combined method" for SQUARE without forcing you to obscure the semantic relation between the different WIDENs involved. > My conclusion is that subclassing is a tool for code re-use and > does not conform to our intuitive understanding of types as sets > and subtypes as subsets, making it difficult to design classes at > a conceptual level before actual programming. I disagree. The subset interpretation is intuitive. It can also be used to explain more complex situations of subtyping (like the above SQUARE constraint) in a satisfactory manner. But one must not confuse it with the independent constructor aspect involved. -- Heinz Schmidt hws@icsi.berkeley.edu