2016-09-06 75 views
0

一直玩scalas類型系統,我再次發現自己與它戰鬥。我爲一些簡單的圖形應用程序創建了一個Vector庫,並且對它非常滿意。scala更高類​​型和scalatic平等

現在我想能夠用scalacheck和scalatest測試Vector屬性,到目前爲止這麼好。我現在面臨的問題是,使用double或float時檢查向量的平等並不像我想的那麼簡單。現在我認爲測試框架中最習慣的方式是爲我的矢量類創建一個new Equality。但是讓我們退後一步,看看我的矢量defenitions

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,這樣我只需要兩個隱含值DoubleFloat試圖之間打轉轉:

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的類型,以便將隱含在我的測試用例可以使用任何方式irregardless如果是Vec2Vec3,只要它是Double類型的?

回答

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存在位打擾。