[Grace-core] The Raw and the Cooked - Was Re: Considering Traits as Objects in Grace

James Noble kjx at ecs.vuw.ac.nz
Fri Mar 8 09:47:48 PST 2013


Some context: I liked this when I wrote it yesterday evening,
but thought about it again and liked it less.
then figured - why not post it anyway?



Hi all

so it seems we're fated to return to this question of object inheritance & initialization again:
It actually seems the last "major" feature we have still to design, or at least to be reasonably
happy with. 

Here's a proposal based on some of Marco's idea on placeholders - and some of the other work 
on safer construction / initialization. Kim's comment about Python in his talk today --- a typo
adding another field to an object --- also illustrates the idea that objects should be "structurally static":
once they have been created or constructed or initialised, their structure should *not* change. 
Clearly the "reverse mutation" semantics do not respect this - not only does structure change but
programs can see it change.  I've talked about _static_ rules to prevent this but never wrote 'em down.
The other idea here is that this is a *dynamic* proposal: coming up with static rules to prevent various
errors is a separate task (and I think doable conservatively --- see Marco's paper).  This also
draws on Andrews ideas about separating creation and "initialisation"

So here goes.  The idea is objects when they are first created are *raw*.  Raw objects are basically
nothing but a singularity, a "naked identity".  Any message sent to a raw object, even an attempt
to get a hashcode or test for == *fails with a dynamic error*. pretty much like "doesNotUnderstand"
because basic raw objects don't have any methods.  A Raw singularity (aka a placeholder) is created
at the start of an object constructor and bound to "self" in that constructor.  This means object 
constructors can only see lexical bindings, *not* methods from the objects being defined or 
any superclasses of that object.   (Except see note below!)    But you can *register* the self of 
an object under construction, and you can make blocks that refer to it and register them -
but any attempt to use the block or registered self will fail while the object is raw.

At some point the raw object becomes cooked, becomes initialized. This could be specified
explicitly, (some reflexive "cook" operation) but I'd rather have it implicit: an raw object
that has had some state added to it becomes cooked when it first recieves a message request.
So once you've had fields added, if you then get a message, you're now cooked, and that
message goes ahead OK.   The catch with cooked objects is that they cannot now have
their structure changed - because that change would now be observable. 

This is where inheritance aka trait composition comes in.  Assuming for now we stick
with the "inherits other" syntax --- how I imagine this working is as a *merge* of 
two (or more) *raw* objects, merging their fields, methods *and identities*.   This 
merge could be a simple override as now, it could be fancier if we really want to
go to the full trait algebra. The details of that aren't important - the key point that the
we only merge raw objects, and we merge their identities (think the Union-Find algorithm). 
Because identities of raw objects are so opaque they cannot even be compared,
it is never visible that they were once distinct, or that their structure has changed.

And that's pretty much it.     For practical reasons, we probably still want a
 "definitively static" rule aka C++ const or (more likely) Dart static:
we know thats doable, we just have to pick one.  The restrictions on raw
objects should be strong enough that inlining etc could mean they are
basically created in toto and always have the "final" identity (e.g. for a JVM,
or really for almost any implementation). 


So what to people think (not just Andrew!)? 
Looking at existing Grace code, are the restrictions on raw objects too strong?
How much code would this break?    Is that a good or a bad thing? 

And break code it will:   my parser combinators, for one example: we want to write

def foo = rule { xx }
def bar = rule { yy }
def baz = foo | bar     // if foo and bar are raw or placeholders, can't request "|" on foo.  oops. 

(this was when I started to think it was a bad idea --- the restrictions
on what code can do "inside a raw object" are too onerous)




note 1 traits  -  if we wanted, we could introduce stateless traits, written

trait foo {
 method bar
}

ideally with Andrew's requirements or statefullness.   In this scheme they would
be "translated" to 

method foo { 
  return object {
      method bar 
   }
}

so they always return something raw, but the individual raw parts would not be created
individually in most implementations...


note 2  inheriting from imported objects   -  imports (and dialect objects) would usually
come in as cooked. if we wanted to extend a module, however, we could just pervert
the import system one again, and do e.g.

import  "raw:StaticGrace"  as rawStaticGrace 

which would bring in the object as raw. Each such import would bring in a *new* raw instance.
Then you can inherit from that raw instances as you like. 


note 3 if we like, we can go to the full placeholders, where defs and vars inside objects
are all originally bound to raw placeholders, initialization of some circular structures
of mutualy-recursive defs


note 4 - structurally static breaks the story that the repl is just inside a normal grace object.
Repls where you can incrementally define variables etc aren't structurally static.  SO we'd 
have to cheat for repls.  I'm not sure about top-level modules / scripts - would they still
work, or not?  

note 5 - we may be able to do something with raw methods.

e.g. for parser combinators, we can't write foo | bar  if foo is a placeholder,
but we could call a lexically bound method  opt(foo,bar) - taking 'em both as placeholders,
presumably constructing a new (placeholder) and initialising it with foo & bar
but not actually cooking the object. BUT that is only structurally static 
if the raw method is *final*...   






More information about the Grace-core mailing list