Вы можете сделать то, что Марк Харра делает в 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)
}
То есть, нет члена типа для следующего типа. Там могут быть вещи, которые вы не можете сделать, как
это ... вы заметите, что HList up не является ковариантным по этой причине .
Мне бы очень хотелось, чтобы кто-то мог указать общий способ сделать тип
члены ковариантны. Я боюсь, что причина, почему они не находятся над моей головой, хотя это может
как-то связано с этим предложением из статьи Мартина Одеркси :
Значение членов
всегда вести себя ковариантно; член типа становится инвариантным как
как только это будет сделано бетоном. Это связано с тем, что Скалина
не допускает позднего связывания для членов типа.
Хотя, если бы кто-то мог объяснить мне это предложение, я был бы рад;)
Редактировать: вот еще один подход, который ближе к тому, что вы изначально просили. На
написав это, я понял, что не уверен, что это действительно будет делать то, что вы хотите ...
может быть, вы могли бы привести пример того, как вы собираетесь использовать эти кортежи?
Поскольку у нас не может быть ковариантных членов типа, мы можем поместить логику "следующего кортежа"
в отдельную черту:
trait Add {
type N[T]
type Add2[T] <: Add
def add[T](x: T): N[T]
def nextAdd[T](n: N[T]): Add2[T]
}
А затем неявно преобразовать в него:
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)
Это общая техника, которую я нашел полезной: Скала не жалуется, если вы
неявно преобразуйте ковариантный тип в инвариантный тип.
Это позволяет вам сделать на 2 вещи больше, чем вы могли бы делать с обычными кортежами:
1) Построить пошаговый кортеж вручную и сохранить информацию о типе:
> val a = () add 1 add 2
> a._1
1
> a._2
2
2) Динамически создать кортеж и, к сожалению, потерять информацию о типе:
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)
Что мы действительно хотели бы сделать
было бы написано
trait Add {
type N[T] <% Add
def add[T](x: T): N[T]
}
То есть убедитесь, что после добавления 1 элемента результат может иметь больше
вещи, добавленные к этому; иначе мы не могли бы динамически создавать кортежи.
К сожалению, Scala не принимает границы просмотра для членов типа. К счастью,
граница представления - не более чем метод, который выполняет преобразование; так что все мы
нужно вручную указать метод; следовательно nextAdd
.
Это может быть не то, что вы ищете, но, возможно, это даст вам некоторые идеи
как приблизиться к своей цели.