部分型
型Aの値を期待する場所に型Bの式を書けるとき、 AはBの部分型であるという。
以下、型Aが型Bの部分型であることをA <: Bと書く。
変性/variance
型生成子Tについて、共変、反変をそれぞれ以下のように定義する:
-
B <: AならばT<B> <: T<A>が成り立つとき、Tは共変/covariantである -
B <: AならばT<B> >: T<A>が成り立つとき、Tは反変/contravariantである -
共変でも反変でもないとき、不変/invariantである
-
型引数
Tがその型のオブジェクトへの入力の型なら、反変ではない -
型引数
Tがその型のオブジェクトから外部への出力の型なら、共変ではない
型境界/type bound
upper type boundは型パラメータをある型の部分型に制限する機能。 traitやインターフェースなど多くの言語で実装されている。
lower type boundは型パラメータをある型の上位型に制限する機能。 Scalaなどの言語に実装されているが、使い所がよくわからない。
trait List[+A] {
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
共変性の定義から、B <: AであるときList[B] <: List[A]
とならなければならない。
List[A]がAに関して共変となるには、そのすべてのpublicなプロパティもAに関して共変でなければならない。
プロパティprependの型は
A => NonEmptyList[A]
である。
戻り値が共変で、NonEmptyList[B] <: NonEmptyList[A]であるため、
引数がなければ共変になる。
しかし、実際には引数を持ち、関数型は引数に関して反変であるため、
A => NonEmptyList[A]は共変にならない。
そこで、引数にAを取らないようにジェネリック関数にして、
T => NonEmptyList[A]という型にする。
さらに、リストが過度に具体的な型になるのを防ぐため、型制約T >: Aを設定する。