[Grace-core] Factory Methods

James Noble kjx at ecs.vuw.ac.nz
Fri Mar 8 22:20:52 PST 2013


Factory Methods

[Another attempt at initialisation and inheritance.  I really quite
like this one - but then I really like the "Raw and Cooked" version
when I first wrote it too, and, well, that didn't go very far.  Indeed
there are some similarities between these two if you look hard enough.
Hopefully Kim & Andrew at least can read this and we can talk tomorrow
in the snow.]

I think this factory methods design:
 - keeps the current syntax
 - gives Kim's "classical inheritance semantics" 
 - preserves Andrew's classes-to-objects rewriting
 - and even lets Michael "inherit true"

Basically, an object constructor that is called from an "inherits"
clause adds its contents into the object under construction. Each
object constructors' code is run, starting at the superclass/top
object to the subclass/bottom object, all with self bound to the same
identity, and with all the methods/defs/vars that will be in the final
object (although some vars may be uninitialised).  Down calls, blocks,
and registration will all work as we'd like.

The cost is that inheritance clauses now make "factory method
requests" that are slightly different to normal method requests.
Object constructors behave slightly differently when returned from
factory methods. Factory methods invoked from an "inherits" clause
pass along the identity of the object being constructed as an extra
implicit parameter.  For these reasons, factory methods need to be
annotated in objects and in types.

I think this will work. But I'm also damn sure there are corner cases
etc that I haven't worked out.



Syntax:

This design keeps the current syntax:

object { 
  inherits C.F
  ... 
}

but adds the restriction that C.F must be a "factory method request".


A Factory Method Request is a method request that invokes only methods
annotated "is factory" (a factory method). To be a valid factory
method, a method must either:

 * return a tall call to an object constructor

 * return a stateless trait aka value object* 
     (to be defined in another email this evening)

 * call another factory method:
   e.g by
    - doing nothing except a tail call to another factory method
    - tail-calling a factory method called on a lexically bound def
         (that def would also be annotated factory, I guess,
          so that if its overridden it still a factory request,
          or it could be private or final)
    - doing other completely pure stuff (computing parameters)
          and tail-calling a factory (see that other email)

These restriction could be checked either dynamically or statically.
I'm sure they will need to be tweaked a bit too!

The point is that

class C.n {
  ... 
}

now translates into 

def C = object {              // note def - pure, stateless call
  method n is factory {
     returns object {  .. }   // note tail call
  }
}



Semantics:

When a factory method is executed *normally*, it has exactly the same
semantics as any other method request in Grace currently.

When an object constructor is executed *normally*, its semantics are as
follows: (I apologise that this description is horribly
meta-imperative especially because I don't think it needs to be.)

  1. the constructor allocates a new object identity for the object
       under construction.
  2. the constructor adds its own methods, and uninitialised slots
       for vars and defs to that object's identity.
  3. the constructor makes a special **factory method request**
       for its inherits clause **passing the identity of the object
       under construction as an extra implicit parameter**  
  4. the constructor runs its inline code from top to bottom,
       initialising its vars and defs. 

When a **factory method request** invokes another factory method, it
just passes that *extra implicit parameter* along to that factory
method.

When a **factory method request** invokes an object constructor, it
executes as follows:

  1. the constructor gets that *extra implicit parameter* which is the
      identity of the object under construction. (different)
  2. the constructor adds its own methods, and uninitialised slots
       for vars and defs to that identity, resolving overriding and
       ambiguity as necessary (same modulo resolution). 
  3. the constructor calls makes a special factory method request
       for its inherits clause **passing the same identity that it
                                 received as an extra 
       	       		         implicit parameter**  (same)
  4. the constructor runs its own inline code from top to bottom,
       initialising its the vars and defs. (same)

Finally, when **factory method request** would return a pre-existing
stateless trait or value object, it copies the trait's methods and
defs into the object under construction, whose identity is that *extra
implicit parameter*



Implementation:  

You could implement these calls as I've described above, but it will
be slow. But everything here is static: it should be possible to
flatten all the inheritance out at compile time, allocate a single
chunk of memory, and run the initialisers top down, making this scheme
no more expensive than most other languages.  You could compile two
versions of all factory methods, one normal, another tagged factory,
or you could add parameters & flats to the existing call protocol.  To
do nested classes / virtual classes / family polymorphism properly,
each subclass would need its own flattening of inherited object
constructors.



Quirks: 

There will be quirks. If a variable overrides a method, a superclass
can initialise that variable in a downcall, and then that variable will
be replaced when (if) the 

I think even Andrew's "initialise" protocol should work fine ---
"create" must be a factory method, but "new" is not. It's possible
everything still works even if a factory method does a bunch of
statefull computation, so long as the tail-call is statically
determinable - or perhaps not; I dunno. 

We have to think carefully about exceptions during  initialisation, or
during factory methods. I think they're OK, or no worse than they are
now. It's possible a partially initialised object could be registered
if a subclass constructor throws an exception after the superclass has
registered the object. Throwing an exception during the factory
method chain doesn't allocate anything, so that should be OK.

Extension:

This will work fine for multiple inheritance too --- modulo all the
other design choices for MI.  But those choices are well known and
well explored, whereas the choices for mixing generative object
constructors and classes are not.  Once we've agreed on this, we can
fight about that for years: unlike this fight, that one isn't
important. MI would be banned in most dialects of course, except those
for writing dialects.

Simplicity/Formalism:

It's possible I'm overstating the staticness needed here - perhaps
even more dynamic rules would work. E.g.
 - an extra "field" in the execution context (stack) that says
     if we're a factory invocation or not; and if we are, tracks
     that extra parameter
 - execution preserves that field
 - evaluating an object constructor in a factory call just adds
     stuff into that identity
 - evaluating a trait in a factory call adds the trait in
 - evaluating a normal object in a factory call is a dynamic error.

(then someone just has to prove the static restrictions prevent the
dynamic error - ECOOP 101 :-)


James


More information about the Grace-core mailing list