-
-
Notifications
You must be signed in to change notification settings - Fork 32
Description
After convenient error handling, which is now in the language, the most glaring inconvenience in Par, which makes it feel like a premordial language is its lack of complex pattern-matching and conditions.
Let's take a look at the current state.
Nested patterns:
list.case {
.end! => /* empty */
.item(x) xs => xs.case {
.end! => /* one item */
.item(y) xs => /* two items */
}
}
Matching on multiple things at once:
left.case {
.end! => right.case {
.end! => /* both empty */
.item(y) ys => /* left empty, right non-empty */
}
.item(x) xs => right.case {
.end! => /* left non-empty, right empty */
.item(y) ys => /* both non-empty */
}
}
The above is a small example, but it's clear there are at least two problems with it:
- Navigating the nested
.casesis mentally taxing. - If the two cases where one side is non-empty and the other side is empty need to be treated equally, there is no easy way to write that.
Conditionals are in a bad place too.
This is okay:
IsPrime(n).case {
.true! => ...
.false! => ...
}
But anything more complex starts to become very inconvenient. I bet you can imagine.
One can think, "oh, I'll just introduce And, Or, Not functions, and it's all good". Well, it's not because there won't be any short-circuiting:
And(Condition1(x, y), Condition2(z, w))
Both conditions will get evaluated.
So, here's my proposal to remedy this situation, in multiple steps.
else in .case
This is just a small thing that makes the further features possible. We add an optional else branch to the .case/case expressions and commands:
list.case {
.item(x) xs => /* ... */
else => {
// `list` now has the type:
//
// either {
// .end!
// }
}
}
As you can see, the type of the subject is narrowed in the else branch to only the remaining possibilities.
Enhanced boolean expressions
We introduce special infix keyword operators: and, or, not for combining boolean expressions in a short-circuiting way.
let bool = IsPrime(n) and {Nat.Less(100, n) or Nat.Less(n, 1000)}
Curly braces are already supported for grouping expressions.
Of course, rules of linearity are enforced when short-circuiting. The expression compiles down to simple branching with .case.
if statement
My proposal combines solving complex pattern matching, and short-circuiting conditions, all in one. We introduce an if statement (different from the usual if statement), and build more syntax conveniences from there.
// process syntax
if {
<condition> => {
<process>
}
<condition> => {
<process>
}
<condition> => {
<process>
}
// optional
else => {
<process>
}
}
Now, what do conditions look like? There are several forms:
<expression> is <pattern><condition> and <condition><condition> or <condition><boolean expression>
Boolean expressions are just expressions that evaluate to either { .true!, .false! }. They can be included without the is form.
A boolean expression <expr> is equivalent to the condition <expr> is .true!.
Examples:
if {
list is .item(x) xs and Int.Less(x, 0) => {
// ...
}
list is .item(x) xs => {
// ...
}
list is .end! => {
// ...
}
}
The examples from the beginning of this post become:
if {
list is .end! => /* empty */
list is .item(x) xs and xs is .end! => /* one item */
list is .item(x) xs and xs is .item(y) xs => /* two items */
}
And:
if {
left is .end! and right is .end! => /* both empty */
left is .end! and right is .item(x) xs or
left is .item(x) xs and right is .end! => /* one empty, one non-empty */
left is .item(x) xs and right is .item(y) ys => /* both non-empty */
}
Much better!
Of course, you immediately see one more possible improvement. Instead of:
list is .item(x) xs and xs is .end! =>
you'd like to write:
list is .item(x).end! =>
Understandable, and we will (most likely) have that! But, I'm excluding it from this initial proposal because it is slightly complicated to implement, and I'd like to get this feature in as soon as possible. This will be a very welcome additional extension.
if expressions
Aside from statements, if will also be usable as an expression:
let negation = if {
bool => .false!,
else => .true!,
}
Single condition syntax sugar
There's one situation that deserves extra brevity: an if statement with a single condition and an else. Here's how it looks without an additional syntax sugar:
if {
not String.Equals(method, "GET") => {
// ...handle this case
exit!
}
else => {}
}
Here's an example that falls through:
if {
debugFlag => {
console.print("Logging, logging...")
}
else => {}
}
That's too verbose!
I think this would be better in this case:
if not String.Equals(method, "GET") => {
// ...handle this case
exit!
}
And:
if debugFlag => {
console.print("Logging, logging...")
}