Pattern matching can be used for a variety of purposes, like analysing (case splitting) enumeration types, taking components from record types and even as a simple switch statement.
Let’s start with the last case, the simple switch statement. It would look like this:
fun match_int(n: Int) -> Unit {
match n {
1: print("One");
2: print("Two");
_: print("Too much");
}
}
Then, you can use it to do something depending on the value of an enumeration-typed variable. Forgetting to include a case here would result in a compilation error
type Result {
Failure, Success(value: Int)
}
fun default_to_zero(res: Result) -> Int {
match res {
Failure: 0;
Success(val): val;
}
}
We can also nest patterns. Here we only execute the right-hand side whose corresponding pattern (left-hand side) exactly matches the value we are pattern matching on.
type Reason {
Timeout,
InvalidResponse,
Other(msg: String)
}
type ExplainedResult {
Success(val: Int),
Failure(Reason)
}
fun prettyPrintResult(res: Result) -> {
match res {
Failure(Timeout): println("Error: The connection timed out");
Failure(InvalidResponse): println("Error: Received an invalid response");
Failure(Other(msg)): println("Error: " + msg);
Success(val): println("Result: val");
}
}
Finally, we can use pattern matching in a slightly different way to quickly extract components of a record type.
type Person(firstName: String, lastName: String, age: Int)
fun printPerson(p: Person) {
let Person(firstName, lastName, age) = p;
print(firstName + lastName + ". Age " + toString(age));
}
# or
fun printPersonExplicit(p: Person) {
match p {
Person(firstName, lastName, age):
print(firstName + lastName + ". Age " + toString(age));
}
}
Trying to use the first form with an enumeration type with more than one constructor will result in a compile-time error. If the value on the right-hand side is not from the constructor you’ve given on the left then there is no way to fill out the values that you’ve given on the right.