si potrebbe fare quello che Mark Harrah fa in up:
sealed trait HList
case class HCons[+H, +T <: HList](head: H, tail: T) extends HList
{
def :+:[T](v : T) = HCons(v, this)
}
case object HNil extends HList
{
def :+:[T](v : T) = HCons(v, this)
}
Cioè, non hanno un membro di tipo per il tipo successivo. Potrebbero esserci cose che non puoi fare come ... noterai che up's HList is not covariant for this reason.
Mi piacerebbe davvero che qualcuno indicasse un modo generale per rendere covariante il tipo di membro . Temo che la ragione per cui non sono è al di sopra la mia testa, anche se potrebbe avere qualcosa a che fare con questa frase da Martin Oderksy's paper:
membri di valore si comportano sempre covariantly; un membro del tipo diventa invariato come appena viene reso concreto. Ciò è dovuto al fatto che Scalina non ammette il late-binding per i membri di tipo.
Anche se qualcuno potrebbe spiegare quella frase mi sarei felice;)
Edit: Ecco un altro approccio che è più vicino a quello che originariamente chiesto. Su scrivendolo ho capito che non sono sicuro se questo farà veramente quello che vuoi ... magari potresti dare un esempio di come intendi usare queste tuple?
Dal momento che non siamo in grado di avere membri di tipo covarianti, possiamo mettere la "prossima tupla" logica in un tratto a parte:
trait Add {
type N[T]
type Add2[T] <: Add
def add[T](x: T): N[T]
def nextAdd[T](n: N[T]): Add2[T]
}
E poi convertire implicitamente ad esso:
class Tuple0Add extends Add {
type N[T1] = T1
type Add2[T1] = Tuple1Add[T1]
def add[T1](x: T1) = x
def nextAdd[T1](n: T1) = new Tuple1Add(n)
}
implicit def tuple0Add(t0: Unit) = new Tuple0Add
class Tuple1Add[T1](t1: T1) extends Add {
type N[T2] = (T1, T2)
type Add2[T2] = Nothing
def add[T2](x: T2) = (t1, x)
def nextAdd[T2](n: (T1,T2)) = sys.error("Can't go this far")
}
implicit def tuple1Add[T1](t1: T1) = new Tuple1Add(t1)
Questa è una tecnica generale che ho trovato utile: Scala non si lamenta se si converte implicitamente un tipo covariante in un tipo invariante .
Questo quindi permette di fare 2 cose di cui sopra che cosa si potrebbe fare con le tuple regolari:
1) costruire manualmente una tupla in passi, e conservare le informazioni sul tipo:
> val a =() add 1 add 2
> a._1
1
> a._2
2
2) Costruire un tupla in modo dinamico e, purtroppo, perdere le informazioni sul tipo:
def addAll(a: Add, s: List[_]): Any = s match {
case Nil => a
case x::Nil => a add x
case x::xs => addAll(a.nextAdd(a add x), xs)
}
> addAll((), List(1, 2))
(1, 2)
Quello che veramente sarebbe piaciuto fare sarebbe quello di avere scritto
trait Add {
type N[T] <% Add
def add[T](x: T): N[T]
}
Cioè, affinché dopo l'aggiunta di 1 elemento, il risultato può avere più cose aggiunte ad esso; altrimenti non potremmo costruire tuple dinamicamente. Sfortunatamente, Scala non accetta i limiti di vista sui membri del tipo. Fortunatamente, una vista associata a non è altro che un metodo che esegue la conversione; quindi tutto ciò che dobbiamo fare è specificare manualmente il metodo; quindi nextAdd
.
Questo potrebbe non essere quello che stai cercando, ma forse ti darà alcune idee come avvicinarti al tuo obiettivo attuale.
Posizionerei i tipi di append in una gerarchia di tipi separata che viene risolta utilizzando la risoluzione implicita. In questo modo puoi usare le normali classi di tuple di Scala. –