Hug*_*ira 17 specifications scala category-theory scalacheck
考虑以下类别定义:
trait Category[~>[_, _]] {
def id[A]: A ~> A
def compose[A, B, C](f: A ~> B)(g: B ~> C): A ~> C
}
Run Code Online (Sandbox Code Playgroud)
这是一元函数的实例:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
现在,类别受到一些法律的约束.关于composition(.
)和identity(id
):
forall f: categoryArrow -> id . f == f . id == f
Run Code Online (Sandbox Code Playgroud)
我想用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) }
}
}
Run Code Online (Sandbox Code Playgroud)
但这些是通过(i)特定类型(Int
)和(ii)特定函数(intG
)来量化的.所以这就是我的问题:在推广上述测试方面我能走多远,怎么做?或者,换句话说,是否可以创建任意A => B
函数的生成器,并将它们提供给ScalaCheck?
不完全了解Hilbert的epsilon,我会采用更基本的方法并使用ScalaCheck Arbitrary
并Gen
选择要使用的函数.
首先,为要生成的函数定义基类.通常,可以生成具有未定义结果的函数(例如除以零),因此我们将使用PartialFunction
作为基类.
trait Fn[A, B] extends PartialFunction[A, B] {
def isDefinedAt(a: A) = true
}
Run Code Online (Sandbox Code Playgroud)
现在您可以提供一些实现.覆盖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.
Run Code Online (Sandbox Code Playgroud)
我选择使用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.
Run Code Online (Sandbox Code Playgroud)
现在您需要创建一个生成器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)
Run Code Online (Sandbox Code Playgroud)
现在您可以定义属性.使用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))
}
Run Code Online (Sandbox Code Playgroud)
虽然我所展示的只是概括了测试的功能,但希望这会让你了解如何使用高级类型系统技巧来概括类型.
归档时间: |
|
查看次数: |
830 次 |
最近记录: |