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 between a common part and individual cases requires doing away with mandatory parentheses around conditions. Demarcating the place …
- where the condition ends and the individual branch starts, as well as
- where the individual branch ends and the next 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.
Examples
For the code examples a hypothetical language with indentation-sensitive syntax and the keyword if
(and optionally then
) has been chosen.
Other reasonable keyword choices are match
, switch
, case
or when
.
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 alice
.age < 18 then "18"
is Person("Alice", _) then "{$person.age}"
is Person("Bob", let age) then "$age"
else "0"
pattern matching using “if-let”12
if person is Person("Alice", let age) then "$age" else "o"
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