3 ms·
Trailing commas avoid having the last item behave differently, but they're easy to accidentally omit when they're optional. [ "or" , "alternatively" , [
by kragen 1y ago
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-pascal.html https://www.cs.virginia.edu/~evans/cs655/readings/bwk-on-pas....
In Meta5ix http://www.canonical.org/~kragen/sw/dev3/meta5ixrun.py 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 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.
- stevage 1y agoThis still seems to have the problem that the first term behaves differently from the others, and is hence inconvenient for editing operations. FWIW with SQL multi-line booleans, I tend to do: WHERE TRUE AND something AND something_else
- kragen 1y agoYes, I agree. That's a nice trick! It's more verbose, which is a tradeoff, maybe a bad one. Too bad `,` doesn't have an identity element the way `and` does, so this doesn't solve the problem in JSON.