Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!uunet!decwrl!shelby!neon!craig From: craig@Neon.Stanford.EDU (Craig D. Chambers) Newsgroups: comp.object Subject: Re: prototypes and degenerate cases Message-ID: <1990Sep13.235832.23454@Neon.Stanford.EDU> Date: 13 Sep 90 23:58:32 GMT References: Organization: Stanford University Lines: 78 I think there's some confusion about dynamic inheritance in Self. In the circle-mutating-into-ellipse example, there would be a *child* object of either a circle object or an ellipse object, and other objects would point to this child. This child would act just like either a circle or an ellipse, because of normal object inheritance (the state and behavior of the parent is inherited, and accessible transparently to clients who can't tell there's an extra child object serving as mediator). Before: circle A | | -----> child change child's parent to turn into an ellipse After: ellipse A | | -----> child To mutate a circle into an ellipse, a new ellipse object would be created with the appropriate state, and the parent of the child object would be redirected to the ellipse object from the circle object. All clients still point to the child, and so the child appears to have "become" an ellipse (or at least its external behavior has changed so that its two foci are no longer the same point). But no weird operations (like mutating the formats of objects or redirecting all external pointers) are happening, just a single assignment to a single (parent) slot. Dynamic inheritance is *much* more structured and clean than become:. We use dynamic inheritance in a structured way in our Self programming. One common example occurs when an object may be in one of several "behavior modes." The canonical example is a window that may be displayed either open or iconified. The window object has an assignable parent pointer that either inherits from normal window behavior or from icon behavior, and can shift from one to the other dynamically as the user iconifies and un-iconifies his windows. The resulting code is simpler to understand and extend since the open and iconified cases are separated (just as object-oriented programming improved procedural programming by factoring out different cases into separate routines selected using dynamic method dispatching). Another use for dynamic inheritance is to implement self-reorganizing data structures. For example, a table might be implemented as a list of associations if it's small, but as it grows it might choose to change itself into a hash table for better performance. This could be implemented most easily by having the user's handle on the collection be a child of either a list object or a hash table object, depending on the representation. The circle-vs-ellipse example is similar in flavor. This class of example is useful where the interface to the object doesn't change, but its internal representation and internal algorithms do change. To sum up, being able to support different representations of the same abstraction (such as polygons vs. rectangles vs. squares) is important, and some languages (like Self, Trellis/Owl, and probably CLOS but not like Smalltalk, C++, or Eiffel) support subclasses whose representation is different from their superclasses'. But it is also important to allow objects to change their implementation or inheritance dynamically to best implement their current behavior. This is an implementation decision that is perfectly reasonable in supporting the object's interface. This is where languages with object inheritance that can change at run-time provide additional "clean" power over class-based languages, whose inheritance patterns are static (in every language I know about) and can only solve this problem using hacks like become: or changing an instance's class. -- Craig Chambers