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{ 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 !=<7code> 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 !
As an example, say 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 and otherwise return the error:
match x v V => printV(v) w W => printW(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 => printV(v) | w W => printW(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 => printV(v) | W => printW(v) !
Using a pre-declared variable it
in groovy-style.
x ? V => printV(it) | W => printW(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 => printV(v) | w => printW(w) !
Omitting Variables and Types
Combinding both, we would get:
x ? v => printV(v) | => printW(v) !
or even
x ? printV(it) | printW(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.printV w W => w.printW 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 ?.printV |.printW !
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 ?.printV !
Could allow omitting the !
since it is clear that we do not handle any of the other cases.
x ?.printV
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 reabality 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.printV | w W => w.printW ! x ? v => v.printV | w => w.printW ! x?.printV |.printW ! x?.printV res := x? str := a == b ? "same" | "different"