Scala Language
Programación a nivel de tipo
Buscar..
Introducción a la programación a nivel de tipo.
Si consideramos una lista heterogénea, en la que los elementos de la lista tienen tipos variados pero conocidos, podría ser deseable poder realizar operaciones en los elementos de la lista colectivamente sin descartar la información de tipo de los elementos. El siguiente ejemplo implementa una operación de mapeo sobre una simple lista heterogénea.
Debido a que el tipo de elemento varía, la clase de operaciones que podemos realizar está restringida a alguna forma de proyección de tipo, por lo que definimos una característica de Projection
tiene el type Apply[A]
abstracto type Apply[A]
calculando el tipo de resultado de la proyección, y def apply[A](a: A): Apply[A]
calculando el valor del resultado de la proyección.
trait Projection {
type Apply[A] // <: Any
def apply[A](a: A): Apply[A]
}
En la implementación del type Apply[A]
, estamos programando en el nivel de tipo (a diferencia del nivel de valor).
Nuestro tipo de lista heterogénea define una operación de map
parametrizada por la proyección deseada, así como el tipo de proyección. El resultado de la operación del mapa es abstracto, variará según la clase y la proyección, y, naturalmente, debe seguir siendo un HList
:
sealed trait HList {
type Map[P <: Projection] <: HList
def map[P <: Projection](p: P): Map[P]
}
En el caso de HNil
, la lista heterogénea vacía, el resultado de cualquier proyección siempre será el mismo. Aquí declaramos el trait HNil
como una conveniencia para que podamos escribir HNil
como un tipo en lugar de 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
es la lista heterogénea no vacía. Aquí afirmamos que al aplicar una operación de mapa, el tipo de cabecera resultante es el que resulta de la aplicación de la proyección al valor de cabecera ( P#Apply[H]
), y que el tipo de cola resultante es la que resulta de mapear el proyección sobre la cola ( T#Map[P]
), que se sabe que es una lista 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))
}
La proyección más obvia es realizar algún tipo de operación de HCons[Option[String], HCons[Option[Int], HNil]]
siguiente ejemplo proporciona una instancia de 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)
})