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:


*          *          *          *          *

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.


Grace 0.7 supports traits — reusable collections of methods that can be “mixed in” to class declarations. Classes (and object constructors) can easily use more than one trait. For example, an orderingFromCompare trait could define a suite of ordering methods based on a compare method:

As you can see, a trait declaration looks pretty much like a class declaration, except that it uses the keyword trait rather than class. You can’t see the restrictions on traits: unlike a class, a trait may not contain var or def declarations, nor inline code. Classes can use multiple traits, but can inherit from at most a single superclass.

*          *          *          *          *

It is a truth universally acknowledged that any single inheritance language must want multiple inheritance

Bertrand “Jane Austen” Meyer

We found three reasons to include traits in Grace. First, most other languages to which students may move (including Java, Python, Ruby, and of course Scala, C++ and Eiffel) now support some form of trait or multiple inheritance, and even second year students studying design patterns (like Adapter or Iterator) can benefit from multiple inheritance. Second, we found traits helpful in designing our own libraries—the collections library in particular. Third, we found we needed more flexible forms of reuse when designing dialects. Grace’s dialects form a lattice, not a tree, so we needed some way of composing dialects from reusable components. All of these reasons are more important to library and dialect authors than to beginners: we don’t expect novice students to be taught about traits early on! Grace’s dialect mechanism can be used to remove traits from the language used for beginning students.

*          *          *          *          *

Advanced programmers may note that trait declarations can be though of as methods returning objects, in just the same way that class declarations are methods returning classes.  

The key difference between classes and traits is that classes may declare variables and constants and contain inline code, while traits may not. Another difference is that objects created by classes are automatically furnished with a few default method, including asString and ==, whereas traits are not. Like classes, traits may have type parameters, parameters, and may have multi-part names (although stylistically we try to avoid them).

Any trait declaration can be turned into a class by changing the “trait” keyword to “class”.