一直在玩scalas型系统,我再次发现自己在与它作斗争。我已经为一些简单的图形应用程序创建了一个Vector库,我非常满意。
现在我希望能够使用scalacheck和scalatest测试Vector属性,并且到目前为止一直很好。我现在面临的问题是在使用双精度或浮点数时检查向量相等并不像我想象的那么简单。现在我认为测试框架中最惯用的方法是为我的矢量类创建一个new Equality
。但让我们退一步看看我的矢量防御
abstract class Vec[T, V[T] <: Vec[T, V]](val elems: T*)(implicit num: VecIntegral[T], factory: VecFactory[V])
class Vec2[T](x: T, y: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec2](x,y)
Vec3[T](x: T, y: T, z: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec3](x,y, z)
此处未显示未应用和应用是为Vec对象定义的,允许模式匹配等。
所以现在我第一次尝试编写测试看起来像这样
abstract class VecSuite[T: Arbitrary, V[T] <: Vec[T, V]](implicit genVec: Arbitrary[V[T]], num: VecIntegral[T])
extends PropSpec with PropertyChecks {
import num._
property("associative add") {
forAll { (a: V[T], b: V[T]) =>
assert((a + b).===(b + a))
}
}
property("scalar distributed") {
forAll { (a: V[T], b: V[T], s: T) =>
assert((a + b) * s === a * s + b * s)
}
}
...
}
class Vec2IntSuite extends VecSuite[Int, Vec2]
class Vec2FloatSuite extends VecSuite[Float, Vec2]
class Vec2DoubleSuite extends VecSuite[Double, Vec2]
class Vec2LongSuite extends VecSuite[Long, Vec2]
再次在这里我没有展示,但我为Vec2[T]
和Vec3[T]
类实现了隐式工厂。
所以这很有效。我编写了一般测试,可以将它们应用到我所有不同的受支持的Vector实现中,除了Float
和Double
我得到舍入错误并且我的相等检查爆炸了。
所以现在我开始使用Equality
课程,尝试在Vec2
和Vec2
之间进行通用,这样我只需要Double
和{{}的两个隐含值{1}}尝试:
Float
但这对编译器来说并不合适,而且它会以implicit val doubleEq = new Equality[ V[Double] forSome{ type V[Double] <: Vec[Double, V] }] {
override def areEqual(a: V[Double] forSome {type V[Double] <: Vec[Double, V]}, b: Any): Boolean = (a,b) match {
case (lhs: Vec[Double, _], rhs: Vec[Double, _]) => lhs.elems.zip(rhs.elems).forall {case (e1, e2) => e1 === e2 +- 0.01d }
case _ => false
}
}
爆炸。
有没有办法编写java.lang.StackOverflowException
的类型,以便隐含地在我的测试用例中使用它,无论它是Equality
还是Vec2
,只要它是类型的Vec3
?
答案 0 :(得分:0)
好吧,我实际上解决了这个问题!
class VecDoubleEquality[V[Double] <: Vec[Double, V]] extends Equality[V[Double]] {
override def areEqual(a: V[Double], b: Any): Boolean = (a,b) match {
case (lhs: Vec[Double, V], rhs: Vec[Double, V]) => lhs.elems.zip(rhs.elems).forall {case (e1, e2) => e1 === e2 +- 0.1d }
case _ => false
}
}
implicit def doubleEq[V[Double] <: Vec[Double, V]] = new VecDoubleEquality[V]
这将正确地为我提供具有递归类型的正确类型签名,而不必费心使用forSome
存在位。