Matches in Expressions
We need a compact alternative for match statements that can be used within expression.
Other languages
Rust
Rust uses the postfix ?
operator to obtain a value from
a Result
or an Option
and aborts the current function
in case this value does not exists: The ? operator for easier error handling.
fn read_username_from_file() -> Result<String, io::Error> { let mut f = File::open("username.txt")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) }
C/C++/Java/C#
The C-like languages know the ternary operator b ? a : b
to
distinguish the two values of a boolean expression.
Java
With JEP 305, Java introduced "pattern matching for instanceof" in OpenJDK 14 with a syntax as follows:
if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
While a Java enum can be checked using simple ==
and !=
comparisons.
Python
Python has a few alternatives to the ternary operator:
min = a if a < b else b # equivalent to ternary op a < b ? a : b print( (b, a) [a < b] ) # selecting item from tuple print({True: a, False: b} [a < b]) # using dictionary
Go
Go has type-switches that declare a new variable that can be used for different types:
switch v := i.(type) { case T: // here v has type T case S: // here v has type S default: // no match; here v has the same type as i }
Possible syntax using Operators ?
, |
and !
Assume we have a choice type that can be V or W or some error and we want to work with the value if it is V or W or otherwise return the error:
match x v V => print_v v w W => print_w w x * => return x // pseudo-code, Fuzion does not have return
Using Operators ?
, |
and !
A first step could be to
- allow
x ?
as a short form ofmatch x
- introduce a delimiter like
|
for the cases - introduce an operator like
!
to return an error immediately
So we get:
x ? v V => print_v v | w W => print_w w !
Omitting Variables
We could use the same variable name in all cases, then there it would be sufficient to have a single declaration:
x ? v V => print_v v | W => print_w v !
Using a pre-declared variable it
in groovy-style.
x ? V => print_v it | W => print_w it !
Omitting Types
Next, we can make the types optional and automatically use the types in the order given in the choice type declaration:
x ? v => print_v v | w => print_w w !
Omitting Variables and Types
Combining both, we would get:
x ? v => print_v v | => print_w v !
or even
x ? print_v it | print_w it !
Performing calls on case elements
A common use case would be to perform a call on the elements of a choice (in particular, if there is only one element of interest, and the remaining being errors):
match x v V => v.print_v w W => w.print_w x * => return x // pseudo-code, Fuzion does not have return
This case could be simplified by allowing a dot directly following
the ?
or |
as follows:
x ?.print_v |.print_w !
A Single Case of Interest
If there is only one case of interest, as in an option
or a choice between a value and error cases:
match x v V => v.print x Error => return x // pseudo-code, Fuzion does not have return
The expression form
x ?.print_v !
Could allow omitting the !
since it is clear that we do not handle any of the other cases.
x ?.print_v
Even simpler, if all we are interested in is the value of the first element in a choice type
res := match x v V => v x * => return x // pseudo-code, Fuzion does not have return
we could simplify the expression to
res := x?
Ternary Operation on bool
With these simplifications, a switch over the values of a bool
match a = b t true_ => "same" f false_ => "different"
turns into an expression very similar to the ternary ? :
operator known from C or Java:
a = b ? "same" | "different"
Using |
instead of :
avoids conflicts
with :
used as boolean implication operator (e.g., in pre and
postconditions like pre debug: a.isValid
) or with :
used for inheritance or anonymous inner features.
Solution for Fuzion
Here is what I consider reasonable
- we should not omit variable names, well chosen names help make the code readable and it would become confusing when matching over value vs. errors, strings vs. integer, etc.
- using
it
quickly turns into a readability disaster if you have nested expressions with conflictingit
s, so we should not use them. - omitting the type, however, seems reasonable for small choice types with a
clear order such as
option
orbool
- error handling with
!
that could even be optional for a single case seems essential for readable i/o code without having an exception mechanism
So we end up allowing these expressions:
x ? v V => v.print_v | w W => w.print_w ! x ? v => v.print_v | w => w.print_w ! x?.print_v |.print_w ! x?.print_v res := x? str := a = b ? "same" | "different"