【发布时间】:2014-09-22 11:40:59
【问题描述】:
我正在尝试实现一个非常基本的泛型堆实现,虽然我喜欢类型检查器,但这是我觉得它每一步都在与我作斗争的场合之一。
我能写的最简单的表达式是:
trait Heap[A] {
def isEmpty: Boolean
def merge(as: Heap[A]): Heap[A]
def insert(a: A): Heap[A]
def findMin: A
def deleteMin(): Heap[A]
}
这很好,但只要调用merge、insert 或deleteMin,具体实现就会“松散”它们的类型。也就是说,如果set 的类型为CustomHeap,则调用set.deleteMin 的类型为Heap。
经过一番努力,我想出了解决这个问题的以下定义:
trait Heap[A, Repr <: Heap[A, Repr]] {
def isEmpty: Boolean
def merge(as: Repr): Repr
def insert(a: A): Repr
def findMin: A
def deleteMin(): Repr
}
这开始变得复杂,但可以按预期工作:它是一个通用的 Heap,例如,在调用 merge 时类型不会丢失。
当一个人试图不将自己的代码绑定到Heap 的特定实现时,这个定义有点麻烦,但是:变量不能是Heap[A] 类型,而是更复杂的东西,我很快就放弃尝试编写.
为了解决这个限制,我尝试使用集合 API 中随处可见的 XxxLike 模式,但这就是我卡住的地方。
这就是我目前所拥有的:
trait Heap[A] extends HeapLike[A, Heap[A]]
trait HeapLike[A, +Repr <: HeapLike[A, Repr] with Heap[A]] {
def isEmpty: Boolean
def merge(bs: Repr): Repr
def insert(a: A): Repr
def findMin: A
def deleteMin(): Repr
}
这有点复杂,并且引入了一个新特性,只是为了打字,但我可以忍受 - 如果它有效的话。
在这个实现中,HeapLike 在Repr 上是协变的,但Repr 是merge 的一个参数——在逆变位置的协变类型。我无法解决这个问题。
我还尝试使HeapLike 在Repr 上保持不变,直到我尝试实际混合该特征:
sealed trait LeftistHeap[A] extends Heap[A] with HeapLike[A, LeftistHeap[A]] {
def rank: Int
}
这会产生以下错误消息:
error: illegal inheritance;
self-type this.LeftistHeap[A] does not conform to this.HeapLike[A,this.LeftistHeap[A]]'s selftype this.HeapLike[A,this.LeftistHeap[A]]
sealed trait LeftistHeap[A] extends Heap[A] with HeapLike[A, LeftistHeap[A]] {
我确信有一种简单的方法可以让这一切正常工作 - 它比集合 API 要基本得多,它设法完成所有这些操作并且在包含的元素上保持协变一个集合,但我觉得我撞到了一堵砖墙。有什么建议、解释、指点...?
【问题讨论】:
-
当我尝试编译你的前两个特征(在 scala 2.11 中)时,我收到了这个错误:
<console>:11: error: covariant type Repr occurs in contravariant position in type Repr of value bs def merge(bs: Repr): Repr -
如果你的
Heap是可变的,你所有的变异方法(例如merge、insert和deleteMin的返回类型可能是this.type,而你将为每个实现返回this。这应该让您完全避免复杂的类型签名和抽象Repr类型。 -
@Ashalynd 这很奇怪 - 我刚刚尝试使用 scala 2.11.2 编译我的前两个示例,它们运行良好。第三个确实因协变类型的逆变位置错误而失败,但这在我的帖子中提到了。我误解了你的评论吗?
-
@Dylan my
Heap是不可变的,但我以前从未见过this.type。我会查一下,但是否有特殊原因不能在不可变结构的情况下使用它? -
我尝试编译您的第三个示例(没有 LeftistHeap 特征)。
标签: scala generics covariance contravariance variance