型別級程式設計簡介

如果我們考慮異類列表,其中列表的元素具有變化但已知型別,則可能希望能夠共同地對列表的元素執行操作而不丟棄元素的型別資訊。以下示例在簡單的異構列表上實現對映操作。

因為元素型別不同,我們可以執行的操作類別限於某種形式的型別投影,所以我們定義了一個特徵 Projection,其中抽象 type Apply[A] 計算投影的結果型別def apply[A](a: A): Apply[A] 計算投影的結果

trait Projection {
  type Apply[A] // <: Any
  def apply[A](a: A): Apply[A]
}

在實現 type Apply[A] 時,我們在型別級別程式設計(而不是值級別)。

我們的異構列表型別定義了由所需投影和投影型別引數化的 map 操作。地圖操作的結果是抽象的,將通過實現類和投影而變化,並且自然必須仍然是一個 HList

sealed trait HList {
  type Map[P <: Projection] <: HList
  def map[P <: Projection](p: P): Map[P]
}

HNil 的情況下,空的異類列表,任何投影的結果將始終是它自己。這裡我們宣告 trait HNil 為方便,以便我們可以將 HNil 作為一種型別來代替 HNil.type

sealed trait HNil extends HList
case object HNil extends HNil {
  type Map[P <: Projection] = HNil
  def map[P <: Projection](p: P): Map[P] = HNil
}

HCons 是非空的異類列表。在這裡我們斷言,當應用地圖操作時,產生的頭部型別是由投影到頭部值(P#Apply[H])的應用產生的,並且得到的尾部型別是通過將投影對映到尾部而產生的型別( T#Map[P]),這被稱為 HList

case class HCons[H, T <: HList](head: H, tail: T) extends HList {
  type Map[P <: Projection] = HCons[P#Apply[H], T#Map[P]]
  def map[P <: Projection](p: P): Map[P] = HCons(p.apply(head), tail.map(p))
}

最明顯的此類投影是執行某種形式的包裝操作 - 以下示例生成 HCons[Option[String], HCons[Option[Int], HNil]] 的例項:

HCons("1", HCons(2, HNil)).map(new Projection {
  type Apply[A] = Option[A]
  def apply[A](a: A): Apply[A] = Some(a)
})