Traits

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”.

Classes

Classes in Grace 0.7 are simpler than they used to be. A class declaration creates a “factory method”, that is, a method that returns a new object. Writing:

declares a factory method that returns an object with readable fields
colour and name, a variable that counts the number of mice the cat has
eaten, and two methods to eat a mouse and to miaow. Other than
Grace’s multi-part method names, this syntax is pretty much the same,
say, as Python or Scala, and with pretty much the same semantics.

To create objects from  the class, we just request the method directly:

Note that we don’t need parens around the arguments, because they are already delimited by quotes.  If the arguments had been expressions, then we would need parens.

More advanced programmer might want to know that classes aren’t primitive.  The cat()colour() class declaration above is pretty much equivalent to the following method declaration:


The key difference between this design and earlier and the earliest designs are that in the earlier designs, class declarations made objects that had a single factory method.  The main reason for the change is that the new design is simpler.  Most Grace programs did not use the actual class object; they simply requested the factory method. Programmers can always explicitly make an object if they need one, by putting the class declaration inside an object declaration.  In particular, classes at the top-level of a module are already inside an object: the module object.  There is often no need for another one.

One situation in which an enclosing object is useful is when there are several alternative ways to construct instances.  For example, a graphics library might provide methods r()g()b() and h()s()l() for constructing new colors, as well as a small selection of constants yellow, purple, brown, etc. It makes sense to make all of these methods on a colour object; r()g()b() might be a class, while h()s()l() and the named colors might make requests on r()g()b() with appropriately calculated arguments. Like this:

The old “dotted class” syntax did not support such a use, because the automatically declared object could have only a single factory method.

Ever Onward!

EVER ONWARD — EVER ONWARD!
That’s the spirit that has brought us fame!
We’re big, but bigger we will be
We can’t fail for all can see
That to serve humanity has been our aim!
Our products now are known, in every zone,
Our reputation sparkles like a gem!
We’ve fought our way through — and new
Fields we’re sure to conquer too
For the EVER ONWARD I.B.M.

Keeping up the blog has not been one of our strong points, but we have been making some progress in the last few months. We’ve been working our way through a review of Grace’s design and specification, with a few nontrivial changes:

  • Classes create methods, rather than objects, so no longer have “dots” in their name
  • Calling methods in superclasses relies on aliasing, not “super requests”
  • Traits support stateless reusable components
  • A simpler inheritance model that supports classes inheriting from multiple traits
  • Replacing variadic methods with primitive sequences

We expect to describe these changes in a few blog posts soon, as we are working through a thoroughly revised specification.  We plan to release the specification and implementations around the end of February,  for feedback from the community. We then plan to hold workshops (as we did in 2011) to go over what we hope will get getting close to a 1.0 version of the language design. So: Onward! Ever Onward!