15 Case guards
case allows additional condition to be checked if pattern
matches. This is called case guard. Example:
switch x {
case Int(i) if (i > 0): print_int(i)
case Int(i): print_int(-i)
case _: skip;
}
is the same as:
switch x {
case Int(i): if (i > 0) print_int(i)
else print_int(-i);
case _: skip;
}
Although it might not seem very bright at the first glance it comes in
handy in more complex cases, like:
switch x {
// special case negative Ints
case Int(i) if (i < 0): print_string("small")
// don't special case negative Int2s and Int3s, also treat
// non-negative Ints ordinarily
case Int(i):
case Int2(i):
case Int3(i): print_int(-i)
// ignore the rest
case _: skip;
}
15.1 Name binding and case guards
All variables bound by pattern are available in scope of case guard, for
example:
switch x {
case Int(i) if i > 0: // one can use i in condition
case Bool(_):
// but using i here would be error
print_string("positive or bool");
case _:
skip;
}
However beware cases like:
switch x {
case Int(y) if y > 0:
case String(y) if String::length(y) == 3:
// Compiler thinks y should be bound here, but this code
// fails to typecheck.
skip;
case _:
skip;
}
which should be:
switch x {
case Int(i) if i > 0:
case String(s) if String::length(s) == 3:
// all ok
skip;
case _:
skip;
}
15.2 Case guards and pattern exhaustiveness
Compiler assumes each case guard can fail for purposes of exhaustiveness
warnings, for example it will complain about:
int i;
// ...
switch i {
case x if x > 0: return 1;
case x if x <= 0: return 2;
}
that it is not exhaustive, although it is. The correct way to write this
is:
int i;
// ...
switch i {
case x if x > 0: return 1;
case x: return 2;
}
(although if would be surly more convenient in this case :-).