高阶ScalaCheck
考虑以下类别的定义:
trait Category[~>[_,_]] { def id[A]: A ~> A def compose[A,B,C](f: A ~> B)(g: B ~> C): A ~> C } 这是一个一元函数的实例: object Category { implicit def fCat = new Category[Function1] { def id[A] = identity def compose[A,C](f: A => B)(g: B => C) = g.compose(f) } } 现在,类别受制于一些法律。相关组合(。)和身份(id): forall f: categoryArrow -> id . f == f . id == f 我想用ScalaCheck测试。让我们来看看整数的函数: "Categories" should { import Category._ val intG = { (_ : Int) - 5 } "left identity" ! check { forAll { (a: Int) => fCat.compose(fCat.id[Int])(intG)(a) == intG(a) } } "right identity" ! check { forAll { (a: Int) => fCat.compose(intG)(fCat.id)(a) == intG(a) } } } 但是这些是通过(i)特定类型(Int)和(ii)特定函数(intG)量化的。所以这是我的问题:在推广上述测试方面我可以走多远,怎么办?或者换句话说,是否可以创建任意的A => B功能,并提供给ScalaCheck? 解决方法
不知道与希尔伯特的epsilon是,我会采取一个更基础的方法,并使用ScalaCheck的
Arbitrary 和
Gen 选择要使用的功能。
首先,为要生成的函数定义一个基类。一般来说,可以生成具有未定义结果(例如除以零)的函数,因此我们将使用 trait Fn[A,B] extends PartialFunction[A,B] { def isDefinedAt(a: A) = true } 现在你可以提供一些实现。覆盖toString所以ScalaCheck的错误消息是可理解的。 object Identity extends Fn[Int,Int] { def apply(a: Int) = a override def toString = "a" } object Square extends Fn[Int,Int] { def apply(a: Int) = a * a override def toString = "a * a" } // etc. 我选择使用案例类从二进制函数中生成一元函数,并向构造函数传递其他参数。不是唯一的办法,但我觉得最直接。 case class Summation(b: Int) extends Fn[Int,Int] { def apply(a: Int) = a + b override def toString = "a + %d".format(b) } case class Quotient(b: Int) extends Fn[Int,Int] { def apply(a: Int) = a / b override def isDefinedAt(a: Int) = b != 0 override def toString = "a / %d".format(b) } // etc. 现在您需要创建一个Fn [Int,Int]的生成器,并将其定义为隐式任意[Fn [Int,Int]]。您可以继续添加生成器,直到您在脸部为蓝色(多项式,从简单的组合复杂的功能等)。 val funcs = for { b <- arbitrary[Int] factory <- Gen.oneOf[Int => Fn[Int,Int]]( Summation(_),Difference(_),Product(_),Sum(_),Quotient(_),InvDifference(_),InvQuotient(_),(_: Int) => Square,(_: Int) => Identity) } yield factory(b) implicit def arbFunc: Arbitrary[Fn[Int,Int]] = Arbitrary(funcs) 现在您可以定义属性。使用intG.isDefinedAt(a)避免未定义的结果。 property("left identity simple funcs") = forAll { (a: Int,intG: Fn[Int,Int]) => intG.isDefinedAt(a) ==> (fCat.compose(fCat.id[Int])(intG)(a) == intG(a)) } property("right identity simple funcs") = forAll { (a: Int,Int]) => intG.isDefinedAt(a) ==> (fCat.compose(intG)(fCat.id)(a) == intG(a)) } 虽然我所展示的只是概括了测试的功能,希望这将给你一个想法如何使用高级类型系统诡计来推广类型。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |