[Grace-core] Inheritance and object initialisation

Kim Bruce kim at cs.pomona.edu
Fri Jul 20 14:27:11 PDT 2012


I'm depressed.  I believe that there is no way to define inheritance for objects that gives what I consider to be the right behavior for classes.  It seems to me that the problem is that after an object comes into existence, it may mutate in many ways and when you extend it, presumably you are extending the current state of the object, so re-running constructor code would be wrong.

So now the issue for me is whether or not there is a reasonable way of defining inheritance for objects that is simple to understand and that provides useful behavior.  (If not, we might need to revisit the decision to have inheritance on objects).  Suppose we want to interpret the following expression where a represents an object

  object {
      inherits a
      var x:T
      method m(...)->U is overriding {newmBody}
      method n(...)->U' {newnBody }
      someExecutableCode
}

Here are the semantic alternatives that I see, though I would welcome other suggestions.  

1.  Make a shallow clone of a's state (instance variables and definitions), add slots for the new variables, definitions, and methods (replacing overridden methods) and then execute the body of the new object definition (i.e., treat it all as executable code).

2.  Some thing, but make some other kind of clone of a.  Perhaps have a default clone method on each object that can be overridden so that one could make the clone as deep as desired.

So, no re-execution of a's original code at all, just grab the current state, definitions, and methods, copy them (somehow) into a new object and then execute the code in the new object definition.

The advantage/disadvantage of the first is that the object and its subobject both refer to the same objects as fields.  That may be what is desired or may not. If those objects are mutable, then changes to the subobject may result in changes to the superobject.  (Imagine graphics objects with an instance variable, upperLeftCorner.  Moving the subobject would move the superobject as well if the position were mutable.)

The second alternative does give more flexibility, but seems to require an early decision on the meaning of clone that would be binding on all subobjects created via inheritance.  One thing that could be done with the first semantic alternative is to simply replace "inherits a" in the code above by "inherits a.cloneMeth" where cloneMeth is some method (there may be several) creating some kind of clone for a.  Then the inheriting object could choose what kind of clone they want to use.

One problem here for me is that I don't have good intuition as to typical examples where people might want to create subobjects from existing objects (as opposed to creating subobjects from classes).  Perhaps someone can help me here with examples that will improve my intuition.

Here is the way that I would typically use objects/classes when I'm writing a program:
1.  Define an object because I am only expecting to use one of them or because I want to prototype.  Two quick examples of these are
	a.  I create a window to put my graphics in.
	b.  I am writing a program using cars, so I define a quick prototype to get it to look right and make sure the code works.
2.  Generalize that object to a class so I can get lots of objects that differ in some way (e.g., vehicles appearing in different locations in the window).
3.  Realize that I want some variants of these objects that share similar behavior, but that are not available from the original class by adding more parameters or via other simple changes.  So, I lift some of the code of the original class to an abstract superclass and create two (concrete) subclasses, one representing the original objects, the second representing the new variants.  In my example, perhaps motorcycles that can ride between cars.

The main other place I use inheritance involves starting with existing library code and modifying it to provide new or different functionality.

However, each of these would involve extending classes, not objects.  In particular, I'm virtually never interested in initializing new objects with state shared with another superobject.  Almost always, I want distinct new objects composing the state of my new object.

The most serious problem I have with all of this is that generalizing this new semantics to classes gives me the wrong behavior in the most common cases I can think of where this semantics differs from standard (Java) semantics.  If I write:
class C(...) -> T {
    inherits B.new(...)
    method m(...)->U is overriding {newmBody}
    ...
}

then I want the constructor for B to run in an environment where self refers to objects from C (i.e., of type T), not those from B.  I.e.,  I typically want it to allocate the space for the object (including methods), and then run the code in B.new in an environment where calling self.m invokes the new overriding code rather than the original.  Here is an example from my graphics library code where the current (non-traditional) semantics caused me problems:

// class representing objects that manage graphics on screen
class CanvasMaker.new(width':Number,height':Number,
         applet':Applet)->Canvas {
	...
   // make sure events resulting from mouse actions are routed to graphicApp to be handled
   method doSetUp(graphicApp:GraphicApplication) -> Void {
      applet.onSetup {
        applet.size(width,height)
        applet.background(applet.color(255))
      }
      def userMouse = applet.mouse
      userMouse.onClick{
        graphicApp.onMouseClick(LocationClass.new(userMouse.x,userMouse.y))
      }
      userMouse.onPress{
       graphicApp.onMousePress(LocationClass.new(userMouse.x,userMouse.y))
      }
      userMouse.onRelease{
       graphicApp.onMouseRelease(LocationClass.new(userMouse.x,userMouse.y))
      }
      userMouse.onDrag{
       graphicApp.onMouseDrag(LocationClass.new(userMouse.x,userMouse.y))
      }

      applet.start
   }

This basically sets things up so that mouse actions on a canvas are passed along to the graphicApp object.  Ideally this would be used as follows:
class GraphicApplicationStarter.new(width':Number,height':Number)
               ->GraphicApplication{
   def canvas:Canvas = CanvasMaker.new(width',height',processing.applet)
   canvas.doSetUp(self)  // set up the mouse handling to call the correct methods in the application
   ...
}

Notice that self is the parameter to the call to doSetUp.
GraphicApplication is designed to be an abstract class that sets up graphics applications.  Real programs are extensions.  Here is one:

// Application that plays boxball game
class BoxBall.new(width':Number,height':Number)
                     ->GraphicApplication{
   inherits GraphicApplicationStarter.new(width',height')
   canvas.doSetUp(self)  // required here instead of superclass -- grrr!
   ...
   // Drops the ball when mouse is clicked.  If ball drops inside basket
   // then the box moves to a new position
   method onMouseClick(mousePoint:Location)->Void{ ... }

}

This real program has methods to onMouseClick, onMousePress, etc. to handle user actions during the execution of the program.  You'll notice that the second line inside { }'s is a call of canvas.doSetUp(self).  That is there because currently inheritance does not have the desired semantics.  That is, when GraphicApplicationStarter.new(width',height') is executed, the value of self is an object from the superclass rather than the subclass.  Thus when a mouse is clicked, it calls the mouse handling code in GraphicApplication (which is just a stub) rather than the desired code in BoxBall.

I would love to have someone show me examples where it is an advantage to have self mean the superclass's self when running the constructor for the superclass in the midst of constructing an object of the subclass.  If there are advantages and disadvantages to each, then it would be easier for me to accept this semantics.

Meanwhile, what can I do to examples like this work:
1.  Do as above, require all subclasses to start with an invocation of canvas.doSetUp
2.  Write an initialization method that must be sent from the outside to every object created from a subclass.  E.g.,
           newObj = BoxBall.new(...).setUp

Both of these are error prone because programmers will forget them, whereas in more traditional semantics, I can just include it in the superclass code.

A problem I don't know how to overcome at all(though perhaps less common) is what to do with statements like
    def x is readable = self.m(...)    // self added just for emphasis

where m is a method of the class that is then overridden in the subclass.  Because it is a def, there is no way I can go back and fix it once it is set to the wrong value when the superclass constructor is run.  I can make it do what I want by making x a variable and then resetting it, but that defeats the purpose of having defs.

In summary, there are the kind of problems that I am having with the proposed definition(s) of inheritance from objects.  I would really like to see some compelling examples
(1) of where object inheritance is clearly the right thing to do (rather than having objects inherit from classes)
(2) of where the new inheritance semantics makes it easier/better to use inheritance with classes.

Without those I'm having a hard time trying to figure out why giving up traditional class inheritance semantics is a win for Grace.

Kim





More information about the Grace-core mailing list