[Grace-core] Dialect Design Proposal

Timothy Jones tim at zimothy.com
Wed Nov 28 16:00:48 PST 2012


tl;dr Make constructors the fundamental OO concept in Grace.

> I've had occasion to learn quite a lot about JavaScript, and I don't
> believe that JavaScript has anything Grace should emulate. I believe
> the following summary is accurate and fair:
>
>   Idiomatic JavaScript uses class-based inheritance.

I think Javascript is a lesson in how to implement useful and
interesting ideas poorly. The very fact that we *can* use class-based
inheritance in a prototype-based language should be alerting us that
there is something useful in its feature set.

> Natively, JavaScript supports a form of prototype inheritance, but
> it's not a cleanly (and intentionally) designed system like in Self.
> Instead of every object having one or more parent objects to delegate
> to, JavaScript objects have a "constructor" function which has an
> prototype object, and prototype objects can be singly linked in a chain.
> It's an adequate solution, but nobody seems to like it in practice.
> Personally, I find it baroque and confusing.

It's interesting to investigate why Javascript's object system is
terrible (because it is if you want to emulate classes), so that we
might retrieve a more graceful solution. The reason we can emulate
class-based inheritance in Javascript is because it describes objects in
terms of constructors, rather than literals. When a super-constructor is
invoked, we can manually apply the value of `this` inside the
invocation, and thus avoid the problem where the self value of the
super-object is applied in any inherited methods. In theory this is our
goal, but because of the prototype chain we end up with a complete mess
instead. Here's some code from the Grace backend I've been working on:


function AbstractError(name, message) {
    this.name = name;
    this.message = message;
}

function Error(name, message) {
    AbstractError.call(this, name, message);
}

Error.prototype = new AbstractError();


The problem here is that in order to make Error an instance of
AbstractError, we need to have an instance of AbstractError as the
prototype, but we don't yet have the instance variables, so we end up
with this weird call to the super method before any normal instances are
created. If we have reasonable defaults to pass to this prototype, then
that can be sensible, but it also means that defining a general
`inherits` function is difficult. I know that the call is safe in this
particular instance, but what if the call to the super object performs
some IO?


function Super() {
    console.log("Inherited!");
}

function Sub() {
    Super.call(this);
}

Sub.prototype = new Super();
new Sub();


This obviously prints out "Inherited!" twice, even though I've only
inherited once. In order to implement true class-based inheritance for
any two constructors, we need to define this odd function:


function inherits(Child, Parent) {
    function SafeConstructor() { this.constructor = Child; }
    SafeConstructor.prototype = Parent.prototype;
    Child.prototype = new SafeConstructor();
}


This places another constructor in between the two which doesn't call
into the super method. Then Child can safely call Parent with the right
`this` value once it is actually invoked.


inherits(Sub, Super);
new Sub();


So what have we learnt? That Javascript's object inheritance actually
mixes two separate ideas: Self's prototype delegation (which does not
suffer from the above problem, because the idea is that you've already
created the parent object, but does not allow class-based inheritance)
and dynamic binding of a method's self value. I don't really want to
discuss the pros and cons of the way that Javascript generally binds the
value of `this` because it's another feature of Javascript that is
universally considered idiotic, but this particular ability to assign
the self value in the case of a call to a constructor is clearly useful.

We have already had a discussion about this. Maybe you remember James
bringing up the 'Satanic' translation of factory methods a while back.
That idea never really got off the ground, no doubt because it began
with an acknowledgement that use of the technique was tantamount to
selling your soul, which is fair considering the amount of rewriting the
translation was calling for. I've since become convinced that the only
reason this didn't work was because we were convinced that we needed to
describe factory methods in terms of methods and object literals.
Indeed, our goal has always been to describe what would normally be
built-in functionality as simple syntactic sugar over object literals. I
am myself an advocate of conceptual purity, and consider this a noble
goal. However, our current woes with the object inheritance design mean
that this may not be a tenable solution.

Let's consider some basic goals of the inheritance system:

1. Object inheritance (with correct binding of self)
2. Class-based inheritance (with correct override semantics)
3. Classes defined in terms of objects

The current 'becomes' implementation achieves all three of these goals,
but at what cost? Considering some of the discussion that's been going
on, I might add the incompatible goal of "A sensible description of
immutability" to the list. An object that is declared immutable cannot
be inherited from in the current implementation. To bring the discussion
back to dialects (this did originally start as a discussion of the
dialect system, after all), one of the problems of the current proposal
was that two dialects might wish to inherit from a common super value,
and it didn't make sense for these two objects to merge themselves
together because they are otherwise unrelated. The proposal suggested
that in this case the super dialect should be produced by a class, so
that distinct super objects are created for each inheriting instance.
This works, but it relies on the provider of the super definition to
define a class rather than an object. If it is not defined with a class,
and there is no way to produce a clone of the object, then it is
impossible to inherit in the expected manner. So I think the fourth goal
should be this:

4. Different objects may inherit from the same object, and remain distinct


None of our attempts so far have satisfied all of these goals.
Originally, we weren't able to achieve the second goal, and now we can't
achieve the fourth. It's not clear to me that there is a sensible
solution that achieves all four. So I'm going to suggest moving the
goalposts instead. I like all of these goals, so I just want to make a
slight adjustment to one of them:

3. Object creation defined in terms of a fundamental OO concept

This is a slightly broader interpretation of the goal: our current
fundamental OO concept is the object (literal). As much as I loathe the
idea of introducing new built-in features, I would like to propose is a
different fundamental concept, which we can then use to describe both
objects and classes. The point is that there's no net increase in
fundamental concepts: an object literal is just syntactic sugar over
this new concept.


I'm tentatively (because I know everyone will get angry at me for using
this name) calling this a 'constructor', mostly because 'factory method'
doesn't make a good keyword. For the most part, it translates into an
object nested inside a method.


constructor foo {
    method bar {}
}

// Approximately translates to:
method foo {
    object {
        method bar {}
    }
}


This is slightly less general than the factory method we were originally
proposing, because it immediately enters the object context, which I
think is useful (we don't need to perform any sort of analysis to find
the object the factory creates, for instance). I'm using a keyword
instead of an annotation because this is a fundamental concept: there is
no direct translation using any of the other components of Grace.

Let's say we have an anonymous variant of this constructor (which works
like a block instead of a method). We can then define object literals
like this:


object { method foo {} }

// Can be described exactly by:
constructor { method foo {} }.apply


The magic arises when a call to a constructor appears as the value of an
inherits clause. In this case, rather than creating a new object, the
value of self in the current context is bound to self in the
constructor, exactly as we saw in the Javascript example above. This is
why the translation to a normal method above doesn't quite work, because
Grace doesn't support any way to do this. And rightly so: this is really
the only sensible use of this feature (within the confines of Grace,
anyway) so it makes sense that this is the only place it applies. Now we
can translate classes in terms of just this concept, and have class
based inheritance work as expected.


class Foo.new {
    method bar {}
}

// Can be described exactly by:
def Foo = object {
    constructor new {
        method bar {}
    }
}


So now we have this:


class Top.new {
    method x { print("Top") }
    x
}

class Bottom.new {
    inherits Top.new
    method x is override { print("Bottom") }
}

// Prints "Bottom"
Bottom.new


Or this:


class Top.new {
    method x { print("Top") }
    x
}

// Prints "Bottom"
def bottom = object {
    inherits Top.new
    method x is override { print("Bottom") }
}


Or this:


// Prints "Top"
def top = object {
    method x { print("Top") }
    x
}

// Does not print anything
def bottom = object {
    inherits top
    method x is override { print("Bottom") }
}

// Prints "Top"
top.x
// Prints "Bottom"
bottom.x


Or even this:


// Prints "Top"
def top = object {
    method x { print("Top") }
    x
}

class Bottom.new {
    inherits top
    method x is override { print("Bottom") }
}

// Does not print anything
def bottom = Bottom.new

// Prints "Top"
top.x
// Prints "Bottom"
bottom.x


So object inheritance uses Self-style delegation, but if the
construction of the super object will be at the same time as the
inheriting instance (which is made explicit by the super method being a
constructor), then the chain is set up *before* executing any code in
the constructor itself. Conceptually they all use the same style of
inheritance, the constructor just swaps the order of the setup to ensure
that self is bound as expected. It's like what Javascript does, but
without the incredibly awful setup of the prototype chain before any
object is actually instantiated.

This satisfies our goals! Objects can inherit from objects, classes can
inherit from classes, objects can inherit from classes, classes can even
inherit from objects, they're all defined in terms a fundamental
concept, and inheriting from an object in no way affects the identity of
that object or any other objects inheriting from it. Inheritance works
the way you expect it to work coming from pretty much any other OO
language, you don't have to sell your soul to achieve the
implementation, and most importantly, there's a simple (and true) story
that can be related to students about how inheritance works: delegation.
And if you just want to live in class-land (with a dialect that prevents
inheriting without using a constructor), you don't have to mention this
story at all, because if you only inherit from constructors then the
resulting inheritance is completely indistinguishable from normal class
inheritance. And now we can build a more sensible dialect proposal!

I eagerly await your input on how this solution that appears to solve
all of our problems is somehow utterly flawed.

-- Tim


More information about the Grace-core mailing list