Szukaj…


Wprowadzenie do programowania na poziomie typu

Jeśli weźmiemy pod uwagę listę heterogeniczną, w której elementy listy mają różne, ale znane typy, pożądane może być wspólne wykonywanie operacji na elementach listy bez odrzucania informacji o typie elementów. Poniższy przykład implementuje operację mapowania na prostej liście heterogenicznej.

Ponieważ typ elementu jest różny, klasa operacji, które możemy wykonać, jest ograniczona do jakiejś formy projekcji typu, więc definiujemy cechę Projection o type Apply[A] abstrakcyjnym type Apply[A] obliczając typ wyniku projekcji i def apply[A](a: A): Apply[A] obliczając wartość wyniku projekcji.

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

W implementacji type Apply[A] programujemy na poziomie typu (w przeciwieństwie do poziomu wartości).

Nasz heterogeniczny typ listy definiuje operację map sparametryzowaną przez pożądaną projekcję, a także typ projekcji. Wynik operacji mapy jest abstrakcyjny, będzie się różnić w zależności od implementacji klasy i projekcji, i oczywiście nadal musi być HList :

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

W przypadku HNil , pustej listy heterogenicznej, wynikiem każdej projekcji zawsze będzie sama. Deklarujemy tutaj trait HNil jako wygodę, abyśmy mogli napisać HNil jako typ zamiast 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 to niepusta lista heterogeniczna. Twierdzimy tutaj, że podczas stosowania operacji odwzorowania wynikowy typ głowicy jest tym, który wynika z zastosowania projekcji do wartości głowicy ( P#Apply[H] ), a wynikowy typ ogona jest tym, który wynika z odwzorowania rzut na ogon ( T#Map[P] ), który jest znany jako 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))
}

Najbardziej oczywistym takim rzutowaniem jest wykonanie jakiejś operacji owijania - poniższy przykład daje przykład 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)
})


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow