【发布时间】:2011-07-03 04:51:21
【问题描述】:
在下文中,我将只展示我的 Scala 代码的非常简化的版本。足以说明问题。不必要的代码块将减少到...。
有效的部分
我创建了一个向量库(即用于建模数学向量,而不是 scala.collection.Vector 意义上的向量)。基本特征如下所示:
trait Vec[C] extends Product {
def -(o:Vec[C]):Vec[C] = ...
...
}
我为特定向量创建了许多子类型,例如用于二维向量的Vec2,或专门用于二维向量的Vec2Int。
子类型缩小了某些操作的返回类型。例如,从另一个向量中减去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 类型在减法中明显返回This,转换为Box 类型的类型参数V。可以看到Vec的减法还是返回Vec[C],但是为什么此时编译器不能使用VecLike的减法的返回类型呢?
我该如何解决这个问题?
【问题讨论】:
-
只是一个忠告——我不久前尝试过这样做,但遇到了一些相同的问题,以及专业化问题。我发现编写代码生成器来制作小型向量/矩阵库变得容易得多。
-
@Rex Kerr:感谢您的建议。这是我的第二次尝试。在第一次尝试中,我最终得到了太多特定的类和难以管理的代码。这一次,我将专业化限制为 1、2 和 3 维和五种数值类型。如果我将来需要更多或发现代码库无法管理,我肯定会使用代码生成器。你用的是哪一个?有什么建议吗?
-
我用 Scala 编写了自己的代码生成器,具有 2-4 维的向量和方阵,以及五种最有用的原始类型(Short、Int、Long、Float、Double)。 (实际上,生成器可以生成任意维度,但是生成的库的大小在 4 之后开始变得笨拙。)生成器只有大约 1k 行代码,它完成了我永远无法弄清楚如何正确使用的事情专业化(例如类型之间的转换以及 Long+Float 到 Double 的提升)。祝你好运!与代码生成器相比,它更好地使用代码。
-
@Rex Kerr:非常感谢这些 cmets。令人惊讶的是,我们的想法如此相似——我专门针对与您所做的完全相同的原始类型,并且我还考虑在 Scala 中编写代码生成器。到目前为止,我没有这样做,因为我对这样的任务过于尊重。但是很高兴看到您是如何做到这一点并成功地完成了一个有用的库。
-
@Madoc - 顺便说一句,如果你想看看我做了什么,我不久前上传了一个 alpha 版本到code.google.com/p/shipvl