Scala の Variance の話の続き [Scala]
Scala 2.6.0 で新規追加された existential type と Scala の既存機能の variance annotation の関係について調べたり考えたりしていた。既存の variance annotation のこともよくわかっていなかったなあ。
前回 [1] 書いたように covariant な型に対して出来る操作は基本 get のみである。逆に言うとパラメタ化された型が immutable であれば covariant な型付けは安全だし失うものは何もない。そこで Scala では従来から型パラメタに + を付けることによってコンパイラに covariant なサブタイピングを指示出来る。これを以下のような関数的なリスト構造で確認してみる。まずは + なしから。
scala> abstract class MyList[T] defined class MyList scala> case class MyNil[T] extends MyList[T] defined class MyNil scala> case class MyCons[T](head: T, tail: MyList[T]) extends MyList[T] defined class MyCons scala> val ints = MyCons(123, MyNil[Int]) ints: MyCons[Int] = MyCons(123,MyNil()) scala> val vals: MyCons[AnyVal] = ints <console>:7: error: type mismatch; found : MyCons[Int] required: MyCons[AnyVal] val vals: MyCons[AnyVal] = ints ^
Scala はデフォルトでは invariant なサブタイピングであるため上記はコンパイルエラーとなる。これを以下のようにするとコンパイルを通る。
scala> abstract class MyList[+T] defined class MyList scala> case class MyNil[+T] extends MyList[T] defined class MyNil scala> case class MyCons[+T](head: T, tail: MyList[T]) extends MyList[T] defined class MyCons scala> val ints = MyCons(123, MyNil[Int]) ints: MyCons[Int] = MyCons(123,MyNil()) scala> val vals: MyCons[AnyVal] = ints vals: MyCons[AnyVal] = MyCons(123,MyNil())
うっかり前者のように + なしでクラスを定義してしまっていても Scala 2.6.0 からは以下のように救済(?)できる。
scala> val vals: MyCons[T] forSome { type T <: AnyVal } = ints vals: MyCons[T] forSome { type T <: AnyVal } = MyCons(123,MyNil())
ここで疑問。「『 + 付き』で MyCons を定義していた場合の MyCons[AnyVal] 」と、「『 + 無し』で MyCons を定義していた場合の MyCons[T] forSome { type T <: AnyVal } 」は、型名は違うけど同じ型だと考えてよいのだろうか? 何か違いが出る状況はあるのか? それからひょっとして existential type のみをサポートしていれば従来からの variance annotation は不要になるとも言えるのだろうか? こうした微妙な点がまだよくわからない。
[1] http://blog.so-net.ne.jp/rainyday/2007-07-29
コメント 0