This is a good work! An idea from one of the graydon’s posts that struck me: branching conditions are empirically a common source of bugs, so it’s worth researching into better ways of encoding conditionals.
My personal desiderata for good branching syntax:
unified if/match (this paper ticks this)
dependent patterns (also done here)
first class multi-way cond (a-la Kotlin when), for when you want to check the related conditions
I'm curious how unifying if/match would work in languages that use { for delimiting scopes. The syntax presented in the paper seems very ML focussed (it suits langs with significant whitespace), so I don't see how you could apply it in C-style languages without introducing inconsistent or confusing syntax.
Edit: actually, perhaps it could be done
if something is .foo(x) {
/* ... */
}
if something
is .foo(x) { /* .. */ } <-- possible termination point
is .bar(x) && x is .baz(y) { /* .. */ }
^^
don't terminate
instead, extend matching expression
There just needs to be a rule in the parser that says: any is that follows an is + {}-block adds a case to the current pattern match.
match x {
// This is indented singly
S(y) => {
// This is indented doubly
foo(y);
bar(y)
}
_ => 0,
}
What do you imagine as a syntax without double-indentation? Like this C with a particular indentation style, relying on syntax highlighting to separate the cases?
switch x {
case 0:
foo(x);
return bar(x);
default:
return 0;
}
This style is awful once you start to put braces around case blocks (which you need, if you want to declare variables in the case blocks) because now the close brace has the same indentation as the one at the end of the switch block and so visually appears to be at the end.
This is great! One thing I noticed though is that the conditional split syntax requires infix operators, which is not a problem for their particular language (MLscript) since it has both an infix function call operator (of) and an argument threading operator (|>). I would love to see an adaptation of this syntax for Lisp's prefix syntax.
That's super interesting, I'm tempted to replace the pattern match syntax of my language with something like it.
That said, the fourth motivating example looks too minimal to me, syntax-wise. I can hardly tell what's going on. And as other have already pointed out, a translation to curly brace syntax isn't obvious.
I'm curious why they chose ML-style | for their or-patterns. A spelled out or would fit nicely with and imo.
I'd have to see during actual usage if I clearly understand the meaning of a particular construction. I think the generality is good, while the terseness may be a bit much if it costs reader understanding. But this is also a complaint I have about the whole ML family.
Interesting. I like it; it seems about as sensible as anything else I've seen. One thing I have wondered, when reading about like Ruby's pattern matching / decomposition / binding is how it actually gets compiled out, what's checked in what order ... and they really get into details (which I like) in parts 5.3 & 5.4.
Previous approaches face a key decision: which scrutinee to inspect next. This choice affects
both efficiency and automata size. MacQueen and Baudinet [1985] pointed out that building an
optimal tree automaton with the minimal number of tests is equivalent to the dispatching problem,
which is NP-complete. Scott and Ramsey [2000] found that while automata sizes were similar across
heuristics, performance varied significantly.
Seems pretty similar to Swift's "if let case" (and guard variants).
Consider this example:
if expr is Var(name) and ctx.get(name) is Some(value)
In Swift, it would be:
if let case .var(name), let value = ctx.get(name) {
Considering each of the listed "crucial features of the UCS":
Pattern flowing -- This is the "and" syntax, represented in Swift by ",".
A nested multi-way ‘if’ with interleaved ‘let’. -- This is just , let though it handles Optional<T> specially, so you can't let null.
Conditional splits -- I'm not sure I follow 100%, but I think this is achievable with | or switch/if as an expression (just wrap them in parenthesis).
My experience has been that this sort of syntax is pretty useful, but can be quite unclear when nested. Beyond trivial cases, it's almost always better to flatten the matches to one per line, introducing extra names.
This also reminds me of Python's := assignment expression (walrus operator), which has similar pitfalls.
This is a good work! An idea from one of the graydon’s posts that struck me: branching conditions are empirically a common source of bugs, so it’s worth researching into better ways of encoding conditionals.
My personal desiderata for good branching syntax:
I'm curious how unifying if/match would work in languages that use
{for delimiting scopes. The syntax presented in the paper seems very ML focussed (it suits langs with significant whitespace), so I don't see how you could apply it in C-style languages without introducing inconsistent or confusing syntax.Edit: actually, perhaps it could be done
There just needs to be a rule in the parser that says: any
isthat follows an is +{}-block adds a case to the current pattern match.The top of page 6 shows an example that doesn't rely on whitespaces:
Ah I missed that. I guess that would work!
I think Raku does something like this?
I guess by double-indentation you mean like
What do you imagine as a syntax without double-indentation? Like this C with a particular indentation style, relying on syntax highlighting to separate the cases?
FWIW I specifically set clang-format to get rid of the extra indent:
https://github.com/oils-for-unix/oils/blob/master/.clang-format
So it's like this:
It's obviously a minor thing, but it's also ... nicer. One reason might be that it scans the same as an
if/else.This style is awful once you start to put braces around case blocks (which you need, if you want to declare variables in the case blocks) because now the close brace has the same indentation as the one at the end of the switch block and so visually appears to be at the end.
This is great! One thing I noticed though is that the conditional split syntax requires infix operators, which is not a problem for their particular language (MLscript) since it has both an infix function call operator (
of) and an argument threading operator (|>). I would love to see an adaptation of this syntax for Lisp's prefix syntax.That's super interesting, I'm tempted to replace the pattern match syntax of my language with something like it. That said, the fourth motivating example looks too minimal to me, syntax-wise. I can hardly tell what's going on. And as other have already pointed out, a translation to curly brace syntax isn't obvious.
I'm curious why they chose ML-style
|for their or-patterns. A spelled outorwould fit nicely withandimo.I'd have to see during actual usage if I clearly understand the meaning of a particular construction. I think the generality is good, while the terseness may be a bit much if it costs reader understanding. But this is also a complaint I have about the whole ML family.
Interesting. I like it; it seems about as sensible as anything else I've seen. One thing I have wondered, when reading about like Ruby's pattern matching / decomposition / binding is how it actually gets compiled out, what's checked in what order ... and they really get into details (which I like) in parts 5.3 & 5.4.
Seems pretty similar to Swift's "if let case" (and guard variants).
Consider this example:
In Swift, it would be:
Considering each of the listed "crucial features of the UCS":
, letthough it handlesOptional<T>specially, so you can't let null.|or switch/if as an expression (just wrap them in parenthesis).My experience has been that this sort of syntax is pretty useful, but can be quite unclear when nested. Beyond trivial cases, it's almost always better to flatten the matches to one per line, introducing extra names.
This also reminds me of Python's
:=assignment expression (walrus operator), which has similar pitfalls.