Pattern Matching

Note: Pattern Matching support is currently only partway through it's planned feature support (and has some open bugs to be addressed). More to come!

In addition to the typical if-else style branching construct, Claro offers a more powerful construct called "Pattern Matching". In its simplest form, Pattern Matching can simply be used as a more concise replacement for if-else chains.

Compare the following if-else chain:

Fig 1:


var x = 2;

var sizeStr: string;
if (x <= 1) {
  sizeStr = "small";
} else if (x == 2) {
  sizeStr = "medium";
} else {
  sizeStr = "large";
}

print(sizeStr);

with the comparable match-statement:

Fig 2:


var x = 2;

var sizeStr: string;
match (x) {
  case 1 -> sizeStr = "small";
  case 2 -> sizeStr = "medium";
  case _ -> sizeStr = "large";
}

print(sizeStr);

The match statement takes in a single expression of any type, to be compared against the given cases clauses in order - the first one to successfully match is executed (there is no fallthrough like in a Java or C++ style switch).

"Default" Case

The example above makes use of a "default" case that will match anything that the cases preceding it didn't match.

Fig 3:


var x = 2;

var sizeStr: string;
match (x) {
  case 1 -> sizeStr = "small";
  case 2 -> sizeStr = "medium";
  case _ -> sizeStr = "large";
}

print(sizeStr);

In the context of pattern matching, the _ token represents a "wildcard" pattern. Learn more about this in the Wildcard Patterns section.

Multi-Statement Cases

When a case is matched, the associated code block following the -> and preceding the next case (or until the overall closing }) will all be executed. This code block can contain any number of statements.

Fig 4:


var x = 2;

var sizeStr: string;
match (x) {
  case 1 ->
    sizeStr = "small";
  case 2 ->
    print("More than one line of code is valid.");
    sizeStr = "medium";
  case _ ->
    sizeStr = "large";
}

print(sizeStr);

Output:

More than one line of code is valid.
medium

Patterns Must Not Reference Existing Variables

While this may seem like an arbitrary restriction, this is actually necessary in order to ensure that Claro's static exhaustiveness and case reachability checks are actually guaranteed to be correct. Technically, it would be possible for Claro to loosen this restriction, but this is a conscious, opinionated design choice to limit the number of special cases to keep in mind when writing or reading a match statement.

The following is invalid:

Fig 5:


var x = # ...
 2;
match (2) {
  case x -> print("Matched variable {x}");
  case 1 -> print("1!");
  case _ -> print("default!");
}

Compilation Errors:

match_EX5_example.claro:4: Wildcard Binding Shadows Declared Variable: Names of wildcard bindings in Match case patterns must not shadow any already-declared variable in scope.
  case x -> print("Matched variable {x}");
       ^
match_EX5_example.claro:3: Illegal Duplicate Match Case: All case patterns should be unique within a match block.
match (2) {
       ^
match_EX5_example.claro:3: Illegal Duplicate Match Case: All case patterns should be unique within a match block.
match (2) {
       ^
match_EX5_example.claro:3: Illegal Match Containing Multiple Default Cases: Each match block should contain at most one case matching the `_` wildcard.
match (2) {
       ^
4 Errors

Note: Claro's error messaging is a work in progress - the above error message will be improved.