Type Match
Flix supports a type match construct that enables compile-time pattern matching on the type of a polymorphic value.
For example, we can write a function that inspects the type of its argument:
def inspect(x: a): String = typematch x {
case _: Int32 => "x is an Int32"
case _: String => "x is a String"
case _: _ => "x is neither an Int32 nor a String"
}
def main(): Unit \ IO =
println(inspect(12345));
println(inspect("abc"));
println(inspect(false))
Here the inspect
function pattern matches on the type of the formal parameter
x
using the typematch
construct. For example, if the type of x
is an
Int32
then the function returns the string "x is an Int32"
and so forth.
The typematch
construct is eliminated at compile-time, hence there is no
runtime cost.
As the example shows, the typematch
construct always requires a default case.
This is because Flix has infinitely many types, and a typematch
cannot cover
all of them.
A type match can also inspect more complex types, as the following example shows:
def inspect(x: a): String = typematch x {
case _: List[Int32] => "x is a list of integers"
case _: List[String] => "x is a list of strings"
case _: _ => "x is something else"
}
def main(): Unit \ IO =
println(inspect(1 :: 2 :: 3 :: Nil));
println(inspect("abc" :: "def" :: Nil));
println(inspect(false))
We can also bind values with type match, as the following example shows:
def inspect(x: a): String = typematch x {
case i: Int32 => "${i * i}"
case s: String => String.toUpperCase(s)
case _: _ => "-"
}
def main(): Unit \ IO =
println(inspect(12345));
println(inspect("abc"));
println(inspect(false))
Warning: While type match is a powerful meta-programming construct, it should be sparingly and with great care.
A typical legitimate use case for type match is when we want to work around
limitations imposed by the JVM. For example, the Flix Standard Library use type
match to implement the Array.copyOfRange
function as shown below:
def copyOfRange(_: Region[r2], b: Int32, e: Int32, a: Array[a, r1]): ... =
typematch a {
case arr: Array[Int16, r1] =>
import static java.util.Arrays.copyOfRange(Array[Int16, r1], Int32, Int32): ...
...
case arr: Array[Int32, r1] =>
import static java.util.Arrays.copyOfRange(Array[Int32, r1], Int32, Int32): ...
...
// ... additional cases ...
}
Here type match allows us to call the right overloaded version of
java.util.Arrays.copyOfRange
. Thus Flix programmers can use our version of
copyOfRange
(i.e., Array.copyOfRange
) while underneath the hood, we always
call the most efficient Java version.