gpt4 book ai didi

generics - 类型级编程中的协变

转载 作者:行者123 更新时间:2023-12-04 03:54:01 25 4
gpt4 key购买 nike

我正在尝试创建与 Scala 库中的类型等效的 Tuple 类型,仅使用 :+ 方法通过添加 N+1st 值将 Tuple 扩展为 Tuple - 这样我就可以递归地构造 Tuple :

class Test {
abstract class Tuple {
//protected type Next[_] <: Tuple
//def :+[T](p: T): Next[T]
}

case class Tuple0() extends Tuple {
protected type Next[T] = Tuple1[T]
def :+[T](p: T): Next[T] = Tuple1(p)
}

case class Tuple1[+T1](p1: T1) extends Tuple {
protected type Next[T] = Tuple2[T1, T]
def :+[T](p: T): Next[T] = Tuple2(p1, p)
}

case class Tuple2[+T1, +T2](p1: T1, p2: T2) extends Tuple {
protected type Next[-T] = Nothing
def :+[T](p: T): Next[T] = throw new IndexOutOfBoundsException();
}
}

这可以编译,但是一旦我取消注释 Tuple#Next 的定义,我就会得到:
Test.scala:13: error: covariant type T1 occurs in invariant position in type [T]Test.this.Tuple2[T1,T] of type Next
protected type Next[T] = Tuple2[T1, T]
^
one error found

这是为什么?您能否提供一种解决方法,允许我递归地构建元组(混合的、类型安全的值类型)?

谢谢。

最佳答案

你可以做 Mark Harrah 在 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)
}

也就是说,没有下一个类型的类型成员。可能有些事情你做不到
这个...你会注意到 up's HList is not covariant for this reason .

如果有人能指出一种制作类型的一般方法,我真的很喜欢
成员协变。恐怕他们不在的原因不在我的脑海中,尽管它可能
Martin Oderksy's paper 中的这句话有关:

Value members always behave covariantly; a type member becomes invariant as soon as it is made concrete. This is related to the fact that Scalina does not admit late-binding for type members.



虽然如果有人可以向我解释那句话,我会很高兴;)

编辑:这是另一种更接近您最初要求的方法。在
写它我意识到我不确定这是否真的会做你想要的......
也许您可以举例说明您打算如何使用这些元组?

由于我们不能有协变类型成员,我们可以把“下一个元组”逻辑
变成一个单独的特征:
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)

这是我发现有用的一般技术:Scala 不会提示,如果你
隐式地将协变类型转换为不变类型。

然后,这使您可以做两件事,而不是使用常规元组可以做的事情:

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 不接受类型成员的 View 边界。幸运的是,一个
view bound 只不过是一种进行转换的方法;所以我们都
所要做的就是手动指定方法;因此 nextAdd .

这可能不是你想要的,但也许它会给你一些想法
如何更接近你的实际目标。

关于generics - 类型级编程中的协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8066654/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com