部分型

型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を設定する。