Scala Language
Programowanie na poziomie typu
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)
})