Pattern matching

The match operator allows to match values by pattern.

x = 2
print match x {
  case 1: "One"
  case 2: "Two"
  case "str": "String"
  case _: "Unknown"
}
x = "str"
match x {
  case "": {
    println "Empty string"
  }
  case "str": {
    println "String!"
  }
}

In this case value and type are checking. If none of case branches doesn't match, the body of case _ branch will executes.

In addition to the constant values, you can set variable name to case.

def test(x) = match x {
  case a: "case a: " + a
  case b: "case b: " + b
  case c: "case c: " + c
}
a = 10
b = 20
println test(15)  // case c: 15
println test(20)  // case b: 20
println test("test")  // case c: test

In this case there is two scenarios:

  1. Variable is already defined. Matching to its value.
  2. Variable is not defined. Assign matching value to it and executes body of the case branch.

In the example above, the interpreter sees the first two branches as:

case 10: 
case 20:

For the last branch c variable is not defined, so assign c = x and execute body of the case c branch.

Refinements

case branch may have additional comparison

def test(x) = match x {
  case x if x < 0: "(-∞ .. 0)"
  case x if x > 0: "(0 .. +∞)"
  case x: "0"
}

println test(-10)  //  (-∞ .. 0)
println test(0)  // 0
println test(10)  //  (0 .. +∞)

Matching arrays

To compare elements of arrays, the following syntax is used:

  • case []: executes if there are no elements in array
  • case [a]: executes if an array contains one element
  • case [a :: b]: executes if an array contains two or more elements
  • case [a :: b :: c :: d :: e]: executes if an array contain five or more elements

There are two rules for the last two cases:

  • If variables count matches array elements count - all variables are assigned to the value of the array.
match [0, 1, 2] {
  case [x :: y :: z]: // x = 0, y = 1, z = 2
}
  • If array elements count is greater, then the rest of the array will be assigned to the last variable.
match [0, 1, 2, 3, 4] {
  case [x :: y :: z]: // x = 0, y = 1, z = [2, 3, 4]
}

An example of a recursive output array

def arrayRecursive(arr) = match arr {
  case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]"
  case []: "[]"
  case last: "[" + last + ", []]"
}

println arrayRecursive([1, 2, 3, 4, 5, 6, 7]) // [1, [2, [3, [4, [5, [6, [7, []]]]]]]]

Matching array's value

To compare values of array's elements, the following syntax is used:

  • case (expr1, expr2, expr3): executes if an array contain 3 elements and first element is equal to expr1 result, second element is equal to expr2 and third element is equal to expr3.
  • case (expr1, _): executes if an array contain 2 elements and first element is equal to expr1 result and result of the second element is not importand.

FizzBuzz classical problem can be solved using Pattern Matching:

for i = 1, i <= 100, i++ {
  println match [i % 3 == 0, i % 5 == 0] {
    case (true, false): "Fizz"
    case (false, true): "Buzz"
    case (true, true): "FizzBuzz"
    case _: i
  }
}