[Grace-core] Notes on Grace

Kim Bruce kim at cs.pomona.edu
Fri Dec 7 17:44:50 PST 2012


A longish message with thought since PL Design workshop (which I had to leave Tuesday night.  Here's the table of contents:

1.  Reactions from PLDesign

2.  Delegation instead of prototypes

3.  Abstract classes and classes

4.  Initialization

-------------------

1.  Reactions from PLDesign

Most of the feedback I got about Grace at the meeting was concern about the complexity of supporting both object and class inheritance.  Part of that was due to the fact that we talked about corner cases, but I don't think all of it was.  Perhaps it was also the people I talked to (Mads Torgerson and Gilad Bracha -- also William Cook), but they generally thought one of those two notions was sufficient and weighed in strongly for inheritance with classes as what most people want.  We could still define objects and object factories, but they would not be inheritable.  I know this is not likely to be welcomed, but it would be my choice of I were the king of Grace.

2.  Delegation instead of prototypes

Assuming that the rest of you will not agree to that, let's look at some alternatives.  If we end up with some inheritance-like notion for objects, I believe both Andrew and I are now leaning toward a delegation interpretation.  As a result, I'm going to use the phrase "delegates to" in place of inheritance for objects.  To be clear, if o is an object with instance variable x, and o1 and o1 both delegate to o, then o1 and o2 share the x field with o.  I.e., if o1 assigns to x, then both o1 and o2 see the change.  From this we can get the effect of prototyping (with new copies of instance variables) by cloning the object before delgating to it.  Thus our old semantics would come from writing 
def o1 = object {
                    delegates to o.clone
                   ...}

Of course for stateless objects, these two give the same effect.  This is likely to be the case for modules interpreted as objects, for example.  Even if there were state there, the delegation semantics likely makes the most sense.  By the way, super would work with delegates as usual.  That is, it would call the method in the delegate.

3. Abstract classes and classes

Before talking about classes, one concern that Andrew pointed out to me seems quite serious.  We need to have abstract classes in Grace.  Currently we can't define them without putting in some kind of default method body (which returns something of the appropriate type).  We need to be able to label a method as abstract and have that block the execution (but not definition) of creation code.  That is, abstract classes should not be able to be constructed as is, but their creation/initialization code needs to be executed when a (non-abstract) subclass is created.

So can we define class inheritance in terms of delegation on objects?  We can certainly do something like we did before.  If D inherits from C (which has constructor C.new)

def DMaker = {
      method new(...) {
           delegates to C.new(...)
           ...
      }
}

Delegation makes no difference there, though strictly speaking the semantics is more complicated.  It seems to require that inherited methods be obtained by tracing pointers to superclass objects, which adds inefficiency (just as Smalltalk inheritance is very expensive compared to Java/C++ inheritance using v-tables).  Perhaps you can optimize the links away to get the normal more efficient implementation, but I fear that it might be visible.  I also don't know what to do about modeling abstract classes with this notion.

In the absence of solutions to those problems I'd rather just define class inheritance from scratch to include abstract classes as well as concrete classes and to have rules that would bar the construction of objects from abstract classes except as part of the construction of concrete classes.  That is, I want essentially the Java rules for classes/subclasses/abstract classes.
            
4.  Initialization

On a slightly different issue, Andrew suggests that we distinguish between creation & initialization phases.  In the creation phase, initializers of variables and fields would be executed (and references to self would not be allowed), but all other code in the class body would be executed in the initialization phase.

I'm not sure I'm convinced of the need for two separate phases here.  Aside from the complexity of having to understand the two phases, I'm worried about the special cases where we have a definition that requires more than evaluation of a simple expression.  This happens with circular structures, for example.  As a result, java, for example, allows final instance variables to be initialized in a constructor.  While we could avoid that by changing the field from being a def to a var, or by similarly allowing defs to be initialized in the initialization phase, it seems ungraceful.  If we do have two phases then we will need to do them in the proper order:  execute (recursively) creation and initialization of superclass before that of the subclass.

While my own preference would be to treat all the creation/initialization code as just being executed sequentially in the object/class body, I don't think I have that strong feelings about it (though people could convince me to go a different direction if there were compelling arguments or examples for one direction or the other)

Kim





More information about the Grace-core mailing list