scala higher types and scalatic Equality

时间:2016-09-06 09:23:03

标签: scala scalatest scalacheck higher-kinded-types

一直在玩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实现中,除了FloatDouble我得到舍入错误并且我的相等检查爆炸了。

所以现在我开始使用Equality课程,尝试在Vec2Vec2之间进行通用,这样我只需要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

1 个答案:

答案 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存在位。