Trailing commas avoid having the last item behave differently, but they're easy to accidentally omit when they're optional.
[ "or"
, "alternatively"
, [ "the"
, "first"
, "item"
]
, "could"
, "behave"
, "differently"
]
I feel that, despite its repugnant appearance, this "comma-first" approach is the best tradeoff in languages like JSON where trailing commas are forbidden; the leading `[` is much harder to accidentally omit or insert than the subtle trailing `,`. In Emacs I use js3-mode, a hack of js2-mode to support comma-first syntax.
Comma-first syntax is especially convenient in SQL, which has the forbidden-trailing-comma problem and several analogous problems. In C if I have a long Boolean conjunction
if (unpleasantly long boolean expression &&
another unpleasantly long boolean expression &&
yet another unpleasantly long boolean expression) {
there are several ways to fix it, such as nesting ifs or factoring the expressions into variables or functions. The "comma-first" approach is also visually unappealing for spacing reasons, requiring two extra spaces after the parenthesis:
if ( unpleasantly long boolean expression
&& another unpleasantly long boolean expression
&& yet another unpleasantly long boolean expression
) {
In SQL, C's alternative approaches are not available, and the "comma-first" style is much more natural:
where unpleasantly long boolean expression
and another unpleasantly long boolean expression
and yet another unpleasantly long boolean expression
I do agree, though, that it's better to design languages to avoid this problem, and I think the way to do that is by using item
terminators or item
initiators in a list rather than by using item
separators. That's what C did for statements with `;`, which was a difference from the ALGOL tradition including Pascal, where `;` was a statement separator, with the unpleasant consequences described in
https://www.cs.virginia.edu/~evans/cs655/readings/bwk-on-pas....
In Meta5ix http://www.canonical.org/~kragen/sw/dev3/meta5ixrun.py I experimented with using item initiators for rules in a grammar, like Markdown uses for bulleted lists. I'm not pleased with the rest of the syntactic decisions I tried in Meta5ix, but I do think that one was a good tradeoff; here's about a quarter of the Meta5ix compiler:
- terms: term ["," {continue $choice} term] @choice
- term: (factor {else $seq}, output) [factor {assert}, output] @seq
- factor: string {literal $it}
, "(" terms ")"
, "[" @many terms {continue $many} "]"
Note that, while comma-first layout feels like a gross abuse of a punctuation mark with `,`, it's quite common and natural with `|` in grammars and pattern-matches in languages like ML, where an initial `|` is also permitted; here's an excerpt from my port of μKanren to OCaml (
http://canonical.org/~kragen/sw/dev3/mukanren.ml):
let rec walk (s : env) = function
| Vart (Var x) when Env.mem x s -> walk s (Env.find x s)
| u -> u
I think that's what I should have used in Meta5ix, and I will if I get around to revising it.