Można robić to, co robi Mark Harrah w 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)
}
Oznacza to, że nie ma elementu typu dla następnego typu. Mogą być rzeczy, których nie możesz zrobić, jak to ... zauważysz, że up's HList is not covariant for this reason.
Bardzo bym mi się spodobał, gdyby ktoś mógł wskazać ogólny sposób, aby typ kowariantki typu . Obawiam się, że powodem, dla którego nie jest ponad moją głową, choć może to mieć coś wspólnego z tym zdaniu z Martin Oderksy's paper:
członkowie Wartość zawsze zachowywać covariantly; element typu staje się niezmienny jako wkrótce, gdy zostanie wykonany jako konkretny. Jest to związane z faktem, że Scalina nie przyjmuje późnego wiązania dla członków typu.
Chociaż jeśli ktoś mógłby wyjaśnić mi to zdanie byłbym zachwycony;)
Edycja: Oto kolejny podejście, które jest bliżej do tego, co pierwotnie prosiłem. Na pisząc to zdałem sobie sprawę, nie jestem pewien, czy to naprawdę zrobi to, co chcesz ... może możesz podać przykład, w jaki sposób zamierzasz używać tych krotek?
Ponieważ nie możemy mieć kowariantna członków typ, możemy umieścić „obok krotki” logikę do osobnego cechy:
trait Add {
type N[T]
type Add2[T] <: Add
def add[T](x: T): N[T]
def nextAdd[T](n: N[T]): Add2[T]
}
A potem niejawnie przekonwertować do niego:
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)
Jest to ogólna technika, którą uznałem za użyteczną: Scala nie uskarża się, jeśli użytkownik niejawnie przekształca typ kowariantny w typ niezmienniczy.
To wtedy pozwala zrobić 2 rzeczy ponad to, co można zrobić z regularnych krotek:
1) Budowanie krotki ręcznie w krokach i zachować informacje typu:
> val a =() add 1 add 2
> a._1
1
> a._2
2
2) zbudować krotka dynamicznie i, niestety, tracą informacje typu:
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)
Co naprawdę wolałby zrobić byłoby napisane
trait Add {
type N[T] <% Add
def add[T](x: T): N[T]
}
Oznacza to, upewnić się, że po dodaniu 1 elementu, wynik może wtedy mieć więcej rzeczy dodaje się do niej; w przeciwnym razie nie moglibyśmy dynamicznie budować krotek. Niestety, Scala nie akceptuje granic widoku na członkach typu. Na szczęście granica widoku nie jest niczym więcej jak metodą, która dokonuje konwersji; więc wszystko, co musimy zrobić, to ręcznie określić metodę; stąd nextAdd
.
Może to nie jest to, czego szukasz, ale może przyniesie Ci to kilka pomysłów: , jak zbliżyć się do rzeczywistego celu.
Chciałbym umieścić typy dołączeń w oddzielnej hierarchii typów, która jest rozwiązywana przy użyciu niejawnej rozdzielczości. W ten sposób możesz używać normalnych klas krotek Scala. –