Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

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 of match 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 conflicting its, so we should not use them.
  • omitting the type, however, seems reasonable for small choice types with a clear order such as option or bool
  • 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"
last changed: 2024-11-12