Lessons from Goo

Anyone following recent Lisp dialects is probably aware of Jonathan Bachrach's Goo, a descendant of Dylan that has returned to its Lispy roots. It's not being developed any more, but it's worth studying for a language designer, because it is the best-designed recent Lisp. I've learned a number of things from it that I did not expect.

Easy variables

Goo's internal def is more convenient than let. Because it doesn't affect the indentation of the code that follows it, it makes the common operation of adding or removing a lexical variable much easier. It's annoying that def is distinct from dv, and that the other defining forms have no internal version (especially df). And of course it only works in a seq, so it's not always an option. A def that worked in more contexts would be a big step torward eliminating the common hassle of creating a variable when a value is used twice.

Short names

Short names are a win, of course, but you can go too far. Many of Goo's names - df, <lst> - omit a vowel to save one character, at the cost of making the name unpronounceable. So one-syllable "def" and "list" become "D.F." and "L.S.T." in speech. Names are read aloud often enough that this matters more than the decrease in characters. The authors say "goo opts for pronounceable special forms as much as possible", but some of the most frequently used names aren't.

Short names also have more collisions. Goo used to abbreviate define-slot as ds, but there weren't enough two-letter names to go around. It was later renamed to dp (for define-property) so ds could become define-syntax. I don't think that's common enough to deserve a two-character name, but Goo's names are either full-length or tiny. There's no convention of intermediate length, so defsyntax (or, for that matter, the traditional defmacro, which ds closely resembles) wasn't an option. Flexibility in naming conventions is important.

Speaking of naming conventions, the <lst> convention for classes is annoyingly long. It looks short because you don't pronounce the brackets, but those two extra characters are repeated quite often in type declarations. The problem that motivates this convention is that class names want to be overloaded as constructors. In a language where anything can be callable, this would be easy to avoid: a class is its own constructor, so there's no need for another name.

The shorter the names get, the more important conciseness of other syntax becomes. Parentheses and spaces start to dominate the character count. Getting rid of those takes syntax, which is hazardous stuff. Goo uses a little of it to shorten lambda and arglists.

Research vs. development

Goo suffers from being a compilation research project. The implementors have spent much of their effort on on-the-fly translation to C. The result is reasonably fast, and the ability to write inline C is interesting, but other aspects of the language are rather unfinished. There are other experimental features, like dp, that are awkward in practice. I don't blame the authors for not trying very hard to make the language useful, but if they were, this would not be a good strategy.

op wins

Goo's op macro makes partial application easy, and it's more flexible than curried functions. Every functional language should have it.

SRFI 26 has two kinds of op (called cut and cute), differing in whether the argument subforms are evaluated once or on each call. (Despite the cute name, I had to look up the SRFI number. Unmemorable numbers are SRFIs' biggest problem.) This distinction rarely matters, because the arguments are almost always either variable references or literals. In all the code I've written using op, I have never had one that was expensive or had side effects. As it happens, my op implementation, like Goo's, reevaluates the arguments each time - but I didn't know that until I checked.

3 comments:

  1. If you like Goo you probably want to see RLisp, which is way better ;-)

    ReplyDelete
  2. Where would you want an internal def except in a sequence?

    ReplyDelete
  3. It's not that you want internal DEF specifically, but you often want local variables in places that aren't SEQs. In those cases DEF won't work, so you're stuck with LET instead.

    ReplyDelete

It's OK to comment on old posts.