Scala Language
Programmazione a livello di codice
Ricerca…
Introduzione alla programmazione a livello di carattere
Se consideriamo un elenco eterogeneo, in cui gli elementi della lista hanno tipi diversi ma conosciuti, potrebbe essere desiderabile essere in grado di eseguire collettivamente gli elementi della lista senza scartare le informazioni sul tipo degli elementi. L'esempio seguente implementa un'operazione di mapping su un semplice elenco eterogeneo.
Poiché il tipo di elemento varia, la classe di operazioni che possiamo eseguire è limitata a qualche forma di proiezione del tipo, quindi definiamo un tratto Projection
type Apply[A]
astratto type Apply[A]
calcolando il tipo di risultato della proiezione e def apply[A](a: A): Apply[A]
calcolando il valore risultante della proiezione.
trait Projection {
type Apply[A] // <: Any
def apply[A](a: A): Apply[A]
}
Nell'implementazione del type Apply[A]
stiamo programmando a livello di tipo (al contrario del livello di valore).
Il nostro tipo di lista eterogenea definisce un'operazione della map
parametrizzata dalla proiezione desiderata e dal tipo di proiezione. Il risultato dell'operazione della mappa è astratto, varierà in base alla classe e alla proiezione e, naturalmente, deve essere ancora una HList
:
sealed trait HList {
type Map[P <: Projection] <: HList
def map[P <: Projection](p: P): Map[P]
}
Nel caso di HNil
, la lista eterogenea vuota, il risultato di qualsiasi proiezione sarà sempre se stesso. Qui dichiariamo trait HNil
come una comodità in modo che possiamo scrivere HNil
come un tipo al posto di 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
è la lista eterogenea non vuota. Qui affermiamo che quando si applica un'operazione di mappa, il tipo di testa risultante è quello che risulta dall'applicazione della proiezione al valore della testa ( P#Apply[H]
), e che il tipo di coda risultante è quello che risulta dalla mappatura del proiezione sulla coda ( T#Map[P]
), che è noto per essere una 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 proiezione più ovvia è quella di eseguire una qualche forma di operazione di avvolgimento - l'esempio seguente produce un'istanza di 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)
})