Grace specification draft 0.7: request for comments.

The Grace project team is delighted to announce that we have released the Grace Language Specification Draft Version 0.7.0 for public comment at gracelang.org. The core language specification is accompanied by a draft description of the standard prelude and key modules.

grace-spec-0.7.0.pdf Grace Draft Language Specification 0.7.0 (pdf)
grace-spec-0.7.0.html Grace Draft Language Specification 0.7.0 (html)
grace-prelude-0.7.0.pdf Grace Draft Standard Prelude 0.7.0 (pdf)
grace-prelude-0.7.0.html Grace Draft Standard Prelude 0.7.0 (html)

This specification is very much a draft. We expect the specification itself, and the langauge described by the specification, to continue to change as we work towards a stable version of the language — indeed, we hope that this evolution will be driven by the comments on this and subsequent drafts.

The specification drafts are supported by several implementations (minigrace, Kernan, and Hopper, also available at gracelang.org) none of which implement the full specification at the time of writing. We know these implementations are prototypes, which is why we are primarily interested in feedback on the language specification.

You can give us feedback in a range of ways: by commenting here; by emailling grace-spec@ecs.vuw.ac.nz; or by joining the
Grace community.
We look forwards to hearing from you.

Changes to Generics

The Grace 0.7.0 specification replaces the use of angle brackets for generic type arguments Foo<o> with double square brackets: Foo[[o]].

This change only affects the syntax of the language, not the semantics. Generic arguments remain optional, and if not supplied have the Unknown type.

*          *          *          *          *

The reasons for this change are to make Grace programs easier to read (for people) and to parse (for programs). Using the old syntax, for example, code such as:

could call a one-argument method run passing the result of a generic call a<B,C>(1), or it could call the two-argument method run, with arguments a<B and c>(1).

The new syntax resolves these cases clearly. Compare:

with

*          *          *          *          *

At the time of this post, no Grace implementation supports this new syntax.

Constructors for Collections

Grace 0.7 introduces Lineups – a built-in constructor for immutable iterables. So [1, 2, 3] is an iterable with three elements.  This will enable us to remove variable-arity methods.  For example, the constructors for Set, List, etc in the collections library will now take a single iterable rather than a variable number of arguments.

On the inside, methods that take an iterable as an argument are not very different from those with a variable number of parameters.  The method declaration is different, because the parameter is declared as xs:Iterable<Number> instead of *xs:Number, but we think that this is clearer. In both cases, the type of xs is such that the body of the method can iterate over it and deal with one Number at a time.

From the outside (the requestor’s view), the main change is that variable-arity method requests like

will be replaced by single argument requests like

You can use iterable constructors directly, for example, to initialise variables or constants

but, because we intentionally provide Iterables with a limited protocol, it’s more typical to use them to create collection objects:

*          *          *          *          *

The old design using variable-arity methods initially seemed more “neutral”, because it did not require honouring one particular kind of collections with the privileged of being built in, while others were relegated to a library.  However, if the truth be told, both designs rely on a built-in implementation of some sort of collection.  The old design created these collections implicitly whenever a request was received by a variable arity method; the new design creates the Iterable explicitly before the request is made.  Escape analysis should permit both designs to be optimised either in a JIT or by a whole-program compiler, should that become an issue.  But the new design makes it much simpler to check that methods are requested with the correct number of arguments.

Inheritance and Aliasing Methods

Inheritance in Grace 0.7 uses trait-style aliasing to invoke overridden inherited methods.  For example, a pedigree cat class can inherit from a cat class and provide a new definition for (that is, override) its miaow method. Writing:

declares a new class whose instances contain all the methods from the inherited object, and also a new variable prizes, a new method winner, and an overriding version of the miaow method. The trick, of course, is that the pedigreeCat’s miaow method needs to be able to request the inherited miaow method. This is arranged by a new alias clause in the inherits statement:

The alias clause makes the superclasses method miaow available under the new name. This lets our new, overriding definition of miaow re-use the inherited behaviour by requesting the catMiaow alias.

*          *          *          *          *

The main reasons for this change are that super is a complicated construct, frequently misunderstood by students and even (dare we say it) by some instructors. The syntax super.methodName and self.methodName are confusing. They differ not in the receiver, as the syntax implies, since both statements send the request to self. No, the difference is in the rule used to find the correct method once the receiver has the request. Encapsulation says that this should not be any of the requestor’s business—yet with super, it is. Moreover, the meaning of a super-request depends on the location of the code that is making it; this is a clear violation of referential transparency.

Aliasing is simpler. Aliasing simplifies the operational semantics of Grace, by removing super-requests from the language. At the same time, aliasing is more powerful, since it allows reuse of methods from any ancestor in the inheritance chain. Aliasing also simplifies the implementation. Moreover, aliasing supports inheritance from a composition of traits: in contrast, the meaning of super is problematic if methods with the requested name are inherited from multiple places.

Abolishing super reduces the number of syntactically-different requests to four: (1) explicit requests (o.x), (2) self requests (self.x), (3) outer requests (outer.x), and (4) implicit requests (x), which resolve either to self requests or to outer requests.

Aliasing also supports a simpler definition of inheritance in terms of “prefixing”, which goes back to Simula. The idea is that a series of inheriting class declarations can be consider as equivalent to a single class declaration in which the bodies of the superclasses are prefixed to the declaration of the subclass.

In this example, the behaviour of the bottom class should be the same whether it was declared with inheritance (on the left) or without (on the right). In Grace, of course, this textual prefixing works only when all the declarations are in the same lexical scope, i.e., when they are declared one after another in the same file.

An alias clause adds an method additional declaration (for the new name) into the inheriting object; an overridden declaration is simply omitted.

Grace also supports an exclude clause that excludes inherited attributes from the composite object. Similar mechanisms are used in Smalltalk traits. Eiffel has a deep rename construct, which is similar but different, since it also renames recursive requests in the body of the renamed method.