Z# (Zee-sharp)

A new .NET language

Match

The match expression tries to select the first pattern that matches the specified operand.

Here are some examples of patterns that can be used:

v := 42
x := 24

a := match v
    0 ->        // by literal numerical value
    "" ->       // by literal string value
    x ->        // by variable value
    n: U8 ->    // by type (with var name)
    Str ->      // by type (without var name)
    is Str ->   // use is? (alt)
    s: MyStruct ? s.fld1 = 20 ->  // by type with filter
    o: OtherStruct { fld1: 42, fld2: "42" } -> // by (property) prototype
    _ ->        // default (ignore) case

The syntax continues after the -> which contains the result of the expression when that specific pattern matched. Patterns for values and patterns for types can be used at the same time. Type matching is done base on the underlying #typeid.

s := "42"
a := match s
    "" -> "Empty"
    s: Str -> s
    _ -> ""

Can the chosen variable names inside the match expression shadow those in the outer code? For instance if variable x is used in the outer code, can it be used inside the match as x: U8? No. In the example above s would be shadowed.

This example results in "The Answer" based on the string "42":

s := "42"

a := match s
    "" -> "Empty"
    "0" -> "Zero"
    "42" -> "The Answer"    // this pattern will be chosen
    _ -> ""

// a = "The Answer" (Str)

When more comprehensive logic is required to compute the result, a function can be called to yield that result.

The result is always of one type only (unless perhaps when we support union (   ) types or compound (or) types) where only optional ? or Error ‘!’ can be added by other branches.

Here an example on matching a type pattern with multiple patterns for the same type - but different filters.

s = MyStruct
    field1 = 12

a := match s
    n: U8 -> make42(n)
    x: MyStruct ? x.field1 = 12 -> 42
    x: MyStruct -> 0

// a = 42 (U8)

The two patterns for MyStruct differ in filter. That is why this works.

Match Optional

How to detect an optional value:

o: Opt<U8>  // nothing

a := match o
    is U8 -> o.Str()
    _ -> "Nothing"

Match Array / List

Match expressions on lists? Match on the content of the list, for instance the number of items in the list:

r := match list
    () -> 0     // match empty list
    (x) -> x    // match list with one item.
    (x, ...lst) -> recurse(lst) + x    // recursive sum function

Match Anonymous Type

Match on an anonymous type:

t := (42, "101")
r := match t
    (42, _) -> 0    // ignore one tuple property
    (I32, Str)      // match on types
    (,)             // any tuple with two properties

Match Operators

Implicit is the use of the ‘equals’ operator on native value data types. What if we make that explicit and therefor also allow other operators too.

a := 42
s := match a
    < 10 -> "Smaller than 10"                   // `<` operator
    >= 10 and =< 100 -> "between 10 and 100"    // logical and
    > 100 -> "bigger than 100"                  // `>` operator
    // don't need an _ case, we've covered all numbers

The compiler checks if later patterns are still reachable. TBD: How will the compiler be able to assess if a pattern will be reachable?


TBD: I think we should think about a switch statement - not misuse match.

The match expression always yields a result. It would be nice to be able to use pattern matching without having to return a result. Basically allow a void-match (statement).

log: (txt: Str)
    ...

a := 42
match a
    = 42 -> log("This is the answer!")
    > 100 -> log("bigger than 100")
    I32 -> log("This int is smaller than 100 and its not 42.")

TBD

Replace the match keyword with is to align other pattern matching constructions like: c is >= 'a' and <= 'z' or >= 'A' and <= 'Z'

a := s is
    n: U8 -> make42(n)
    x: MyStruct ? x.field1 = 12 -> 42
    x: MyStruct -> 0

a := 42
s := a is
    < 10 -> "Smaller than 10"                   // `<` operator
    >= 10 and =< 100 -> "between 10 and 100"    // logical and
    > 100 -> "bigger than 100"                  // `>` operator

// boolean result
b := c is (>= 'a' and =< 'z') or (>= 'A' and =< 'Z')

? How to distinguish (parsing: both start with var := var is ... - only diff is the newline) between pattern matching conditional expressions (bool) and selecting a matching case (switch)?


TBD

Match on regex?

s := "person@domain.com"
x := match s
    `@` -> ...
    `[a-z]` -> ...
    _ -> error

TBD

Match on Exception Types as a way to handle Exceptions?

x := match someOperation()
    // how to differentiate from normal result type checking?
    ExceptionType -> ...
    _ -> happy_flow