[Grace-core] Notes on Grace

Andrew P. Black black at cs.pdx.edu
Sat Dec 8 16:11:40 PST 2012


On my way home from 2.16; I have a long layover in Denver.  Here are some reactions to Kim's message:


On 7 Dec 2012, at 17:44 , Kim Bruce wrote:

> Most of the feedback I got about Grace at the meeting was concern about the complexity of supporting both object and class inheritance.  

That's exactly why I want one mechanism.  We changed what inheritance meant, to provide for inheritance of initialization, and in the process broke the ability to re-use collections of methods that cannot be "freshly created".  I know that James keeps on saying "Pick any 2", but I now believe convinced that we can get all of the features that we need, from delegation. 

> 2.  Delegation instead of prototypes
> 
> Thus our old semantics would come from writing 
> def o1 = object {
>                    delegates to o.clone
>                   ...}

That's right, although I might write "delegates others to o.clone" (in the core calculus, not in the surface syntax).  And the current semantics would come from writing 


def o1 = object {
                   delegates others to o
                  ...}

> 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. 

Right again.  Even more importantly, they give the same effect for a stateless object that represents a sharable collection of methods, which is mostly what people use an abstract class for.  Such a sharable collection of methods also happens to model a trait, so we could (in th higher language levels) provide the trait meta-operations like +, - etc.


> 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.  

An abstract class is a class that can't be instantiated.  So right now there is no way to inherit from it, because of the freshness rule.
This is a fatal flaw, I think.  As I pointed out above, if there is no state in the thing being delegated to, the question of whether it is copied or not is irrelevant.  (It's not irrelevant in a "live" programming environment (like Smalltalk's), when there is a possibility of editing the code.  But we are ignoring that issue, I think.

> 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(...)
>           ...
>      }
> }

I'm a bit lost here.  Do you want new to return an object that inherits from C.new, or do you want the implementation of the C.new method itself to delegate to C.new?

Delegation to a stageful object can get either shared state for all of the instances, or separate state for all of the instances, depending on whether you delegate to a specific object, or to a copy.  This is exactly the same rule as everywhere else where you have a choice of sharing or copying.  It's a pretty fundamental thing for students to understand when they are using stageful objects.


> 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).

This is a misconception, I think, but not really relevant to the current discussion.  Smalltalk chose to share method dictionaries rather than copy them down not because of its semantics but because it wanted to support an interactive environment, and to minimize memory footprint.  The semantics could be supported by copy, and indeed, that's how we implemented traits.  But the cost of lookup is irrelevant when 99% of your method lookups hit in the cache. I f they don't hit, then I suspect that lookup will be too slow however we do it.

>  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.

That means adding two more concepts.   I see my proposal to add a trait keyword to be much more modest, since it is just a restriction on an existing concept (an object without state), rather than a new one.

> 
> 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 was actually pointing out that all th languages that I'm aware of do already make this distinction, but don't do it vary clearly.  In the case of Java, maybe that's good, because it's enables a number of researchers to build up their publication count exposing the dangers of code that executes in an uninitialized object.

I think that making this distinction clear is actually a good teaching technique.  Any invariant that the object's methods rely on must be established on object creation.   This may mean, in a few cases, that the invariant will need an explicit test for an uninitialized state.  
It also means that all methods, including the initialize method and the methods that it requests, can rely on the invariant.  So initializers can safely make self requests, but creation code can't.  

Once we are explicit about the two pauses, I think that it will be entirely reasonable to ban self references in the creation code.  This hypothesis remains to be tested, of course.

There is also code in my slides that show how to do the initialization protocol with an "initializable" trait.  

> 
> 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, 

That's what we had.  The problem was that you wanted it to do different things depending on the context in which the object was created.  By putting the initialization code in a method (which is what Java does --- the classname."<init>" method) we get the ability to call it "late", that is, after the sub object has been created.  So you would get the Java semantics, because we would be using essentially the same mechanism.

> 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)


Have I convinced you yet?


	Andrew
 


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailhost.cecs.pdx.edu/mailman/private/grace-core/attachments/20121208/d2dd2031/attachment.html>


More information about the Grace-core mailing list