Match Type
Kazuki Moriyama (森山 和樹)
dottyの型システムに現れた強力な機能の一つにMatch Typeがある。
これによって2系のscalaだと信じられないくらい複雑なコードが必要だったものが簡潔になる。
Match Typeとは
受け取った型にパターンマッチをかけて指定の型にマッピングできる強力な機能である。
TypeScriptのconditional typesに近い。
type ToString[X] = X match
case String => "String"
case Int => "Int"
summon[ToString[String] =:= "String"]
summon[ToString[Int] =:= "Int"]
この様に通常のマッチケース式と同様の形で型をマッピングできる。
やばい。
ちなみにcaseで指定していない型を入れると具体型に還元されないようだがコンパイルは通る。
summon[ToString[Long] <:< Any]
上限境界
Match Typeは上限境界を持つことができる。
例えば以下の状況ではコンパイルが通らない。
type ListLike[X] = X match
case String => List[String]
case Int => List[List[Int]]
def head[X](xs: ListLike[X]) = xs.head
// [error] 19 |def head[X](xs: ListLike[X]) = xs.head
// [error] | ^^^^^^^
// [error] | value head is not a member of ListLike[X]
これは必ずしもListLikeがListであるとは確定していないためである。
Match Typeに上限境界を付けてあげるとコンパイルが通る。
type ListLike[X] <: List[_] = X match
case String => List[String]
case Int => List[List[Int]]
def head[X](xs: ListLike[X]) = xs.head
パターン中の型変数
Match Typeでもパターン中の一部を変数として抜き出すことができる。
これは普通のマッチケース式と同様である。
type Content[X] = X match
case List[a] => a
case Map[?, a] => a
summon[Content[List[Int]] =:= Int]
summon[Content[Map[String, String]] =:= String]
ただしパターン中の変数は小文字で始める必要がある。
型レベル階乗計算
Match Typeを使用すれば複雑な計算をシンプルに記述することができる。
例えば2系だと死ぬほど面倒だった階乗計算も以下のようにかんたん。
type Factorial[X <: Int] <: Int = X match
case 0 => 1
case S[i] => X * Factorial[i]
summon[Factorial[0] =:= 1]
summon[Factorial[3] =:= 6]
終わり
Match TypeはまさにScalaの型レベル計算への革命である。