Language Design: Unified Condition Expressions
Introduction
Published on 2018-01-21. Last updated on 2024-04-01
Idea
Replace the different syntactic forms of
if
statements/expressionsswitch
on valuesmatch
on patterns and pattern guardsif
-let
constructs
with a single, unified condition expression that scales from simple one-liners to complex pattern matches.
Motivation
- Cut the different syntax options down to a single one that is still easily recognizable by users.
- Allow the design to scale seamlessly from simple cases to complicated ones.
Minimizing the number of keywords or turning condition syntax into method calls (like Smalltalk) are non-goals.
Considerations
- Ternary expressions and
if
statements can be fully subsumed byif
expressions. - The difference between
switch
andif
…switch
has a fixed part – whose value is compared using an equality relation against a set of individual valuesif
supports arbitrary conditions – it allows more than two branches only by chainingif
to anelse
- Splitting conditions into a shared, common part and individual condition continuations requires doing away with mandatory parentheses around conditions.
Demarcating the place …- where the shared part of condition ends and the individual continuations starts, as well as
- where a branch ends and the next individual condition starts
… requires either …
- mandatory braces around the branch, or
- a start keyword like
then
and- either end keyword like
end
or,
- or indentation-sensitive syntax
- either end keyword like
… to ensure unambiguous parsing.
- Alternative keyword choices to
if
andthen
arematch
,switch
,case
orwhen
. - Though not strictly required,
...
may be used to make the division between the shared part and the individual case more apparent to the reader.
Examples
For the code examples, a hypothetical language with indentation-sensitive syntax and the keywords if
and then
has been chosen.
simple if expression
if x == 1.0 /* same as */
then "a" if x == 1.0 then "a" else "z"
else "z"
one comparison operator on multiple targets
if x == /* same as */ if x /* same as */
1.0 then "a" == 1.0 then "a" if x == 1.0 then "a"
2.0 then "b" == 2.0 then "b" else if x == 2.0 then "b"
else "z" else "z" else "z"
different comparison operators, equality and identity
if x /* same as */
== 1.0 then "a" if x == 1.0 then "a"
=== NaN then "n" else if x === NaN then "b"
else "z" else "z"
method calls
if xs /* same as */
.isEmpty then "e" if xs.isEmpty then "e"
.contains(0.0) then "n" else if xs.contains(0.0) then "n"
else "z" else "z"
pattern matching (is
), introducing bindings, flow typing
if person
.age < 18 then 18
is Person("Alice", _) then person.age
is Person("Bob", let age) then age
else -1
pattern matching using “if-let”12
if person is Person("Alice", let age) then age else -1
wildcards (_
) and pattern guards
if person /* same as */ if person is
is Person("Alice", _) then "alice" Person("Alice", _) then "alice"
is Person(_, let age) && age >= 18 then "adult" Person(_, let age) && age >= 18 then "adult"
else "minor" else "minor"
Related Work
- Haskell – multi-way if-expressions
- CommonLisp – cond and case