考虑以下类别的定义:
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, B, 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?
发布于 2012-05-21 23:07:44
如果不确切知道希尔伯特的epsilon是什么,我会采取更基本的方法,使用ScalaCheck的Arbitrary和Gen来选择要使用的函数。
首先,为将要生成的函数定义一个基类。通常,可以生成具有未定义结果的函数(例如除以零),因此我们将使用PartialFunction作为基类。
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类从二进制函数生成一元函数,并将额外的参数传递给构造函数。这不是唯一的方法,但我发现它是最简单的。
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]生成器,并将其定义为隐式Arbitrary[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, intG: Fn[Int, Int]) =>
intG.isDefinedAt(a) ==> (fCat.compose(intG)(fCat.id)(a) == intG(a))
}虽然我所展示的只是泛化了测试过的函数,但希望这能让您了解如何使用高级类型系统技巧来泛化该类型。
https://stackoverflow.com/questions/10518015
复制相似问题