scala3: Matchableとパターンマッチ
環境 Scala 3.1.2
scala3では Any
と AnyVal
& AnyRef
の間に Matchable
というタイプが追加された。
A First Look at Types | Scala 3 — Book | Scala Documentation
理由はこちら
何が変わったのか?
この変更は ` immutable に定義したタイプがパターンマッチで不変性が破壊されてしまう事への対処として導入された。
簡単に言うとパターンマッチのセレクター、 C(_)
や _ :C
のような、には Matchable
が実装される必要がある。
一番影響がありそうなのは、型パラメータが Matchable
を実装しないとパターンマッチにでWarningが出る事だろう。
例として以下のようなメソッドがある。
def matchableTest[T](t: T) = t match { case _: Int => println("Int") case _ => println("Other") }
scala2であれば当然問題ない。しかし型パラーメーター T
は Matchable
かどうかの保証がないため、scala3ではWarningになる。
sbt:scala3> compile [info] compiling 1 Scala source to /path/to/target/scala-3.1.2/classes ... [warn] -- [E165] Type Warning: /path/to/src/main/scala/Main.scala:7:11 [warn] 7 | case _: Int => println("Int") [warn] | ^^^^^^ [warn] | pattern selector should be an instance of Matchable,, [warn] | but it has unmatchable type T instead [warn] one warning found
つまり T
を T <: Matchable
としてやる必要がある。
// OK def matchableTest[T <: Matchable](t: T) = t match { case _: Int => println("Int") case _ => println("Other") }
またこの問題は 型 C[T]
をパターンマッチした時も発生する
case class Foo[T](val t: T) def matchableTest2[T](foo: Foo[T]) = foo match { case Foo(i: Int) => println(i) case Foo(_) => println("other")
sbt:scala3> compile [info] compiling 1 Scala source to /path/to/target/scala-3.1.2/classes ... [warn] -- [E165] Type Warning: /path/to/src/main/scala/Main.scala:16:18 [warn] 16 | case Foo(i: Int) => println(i) [warn] | ^^^ [warn] | pattern selector should be an instance of Matchable,, [warn] | but it has unmatchable type T instead [warn] one warning found
また余談だが、 scala2 だと List[Any]
となる以下のような場合 scala3 だと List[Matchable]
になる
scala> List(1, "a") val res1: List[Matchable] = List(1, a)
Matchable
のメンバ
現状 Matchable
はメンバを持たない
abstract class Any: def isInstanceOf def getClass def asInstanceOf // Cast to a new type: myAny.asInstanceOf[String] def == def != def ## // Alias for hashCode def equals def hashCode def toString trait Matchable extends Any class AnyVal extends Any, Matchable class AnyRef extends Any, Matchable
が将来的には getClass
や getInstanceOf
は Matchable
に移動する可能性もあるらしい。
Matchable is currently a marker trait without any methods. Over time we might migrate methods getClass and isInstanceOf to it, since these are closely related to pattern-matching.
-source:future
とscala2互換性
と説明を書いたが、この機能はデフォルトではscala3でも互換性を考慮しWarningを出さない。
scalacOptions
に -source:future
を追加する事で有効になる。