在下文中,我将仅展示我的Scala代码的非常简化版本。足以显示问题。不必要的代码块将减少到...
。
我创建了一个矢量库(用于建模数学向量,而不是scala.collection.Vector
意义上的向量)。基本特征如下:
trait Vec[C] extends Product {
def -(o:Vec[C]):Vec[C] = ...
...
}
我为特定向量创建了许多子类型,例如Vec2
用于二维向量,或Vec2Int
专用于二维Int
向量。
子类型缩小了某些操作的返回类型。例如,从另一个向量中减去Vec2Int
不会返回通用Vec[Int]
,而是更具体的Vec2Int
。
此外,我已将这些方法在Vec2Int
等非常特定的子类型中声明为final
,从而允许编译器选择这些内联方法。
这非常有效,我已经为矢量计算创建了一个快速且可用的库。
在此基础上,我现在想要创建一组类型来模拟基本的几何形状。基本的形状特征如下所示:
trait Shape[C, V <: Vec[C]] extends (V=>Boolean) {
def boundingBox:Box[C,V]
}
Box
将是Shape
的子类型,对n维框进行建模。
现在,我尝试定义框:
trait Box[C, V <: Vec[C]] extends Shape[C,V] {
def lowCorner:V
def highCorner:V
def boundingBox = this
def diagonal:V = highCorner - lowCorner // does not compile
}
diagonal
方法无法编译,因为方法Vec.-
会返回Vec[C]
,而不是V
。
当然,我可以让diagonal
返回Vec[C]
,但这在很多方面都是不可接受的。这一次,我将失去特定Vec
子类型的编译器优化。此外,例如,当您有一个由两个二维Float
向量(Vec2Float
)描述的框时,假设对角线也是Vec2Float
是很有意义的。我不想丢失这些信息。
按照Scala集合层次结构的示例,我引入了一个类型VecLike
:
trait VecLike[C, +This <: VecLike[C,This] with Vec[C]] {
def -(o:Vec[C]):This
...
}
我做了Vec
扩展它:
trait Vec[C] extends Product with VecLike[C, Vec[C]] ...
(然后我会继续创建VecLike
的更具体的子类型,例如Vec2Like
或Vec3Like
,以配合Vec
类型的层次结构。)
现在,Shape
和Box
的新定义如下所示:
trait Shape[C, V <: VecLike[C,V] with Vec[C]] ...
trait Box[C, V <: VecLike[C,V] with Vec[C]] extends Shape[C,V] {
...
def diagonal:V = highCorner - lowCorner
}
仍然,编译器抱怨:
Error: type mismatch;
found: Vec[C]
required: V
这让我很困惑。类型VecLike
在minus方法中明确返回This
,这将转换为V
类型的类型参数Box
。我可以看到Vec
的减号方法仍然返回Vec[C]
,但为什么此时编译器不能使用VecLike
的减号方法的返回类型?
如何解决此问题?
答案 0 :(得分:6)
我的建议是,在省略您认为无关的代码时,不要太努力,只需显示代码即可。人们经常设法删除重要的部分真是太神奇了。口头禅是“如果你不知道为什么它不起作用,那你就不知道什么是相关的。”这是非常认真的,真实的建议:如果你给我编译的代码除了你不理解的东西外,我可以在五秒钟内给你帮助,或者如果我必须重建你所有的碎片,我可以在五分钟内帮助你遗漏了猜猜哪一个经常发生。
代码。在我猜测第一次尝试的位如何填充到第二次尝试之后,它完全按照给定的方式编译。 (这个“猜测”阶段是预先显示代码的另一个好理由。)
trait VecLike[C, +This <: VecLike[C, This] with Vec[C]] {
def -(o: Vec[C]): This
}
trait Vec[C] extends Product with VecLike[C, Vec[C]] { }
trait Shape[C, V <: VecLike[C,V] with Vec[C]] { }
trait Box[C, V <: VecLike[C,V] with Vec[C]] extends Shape[C, V] {
def lowCorner: V
def highCorner: V
def boundingBox = this
def diagonal: V = highCorner - lowCorner
}
% scalac281 a.scala
%