加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

高阶ScalaCheck

发布时间:2020-12-16 09:31:48 所属栏目:安全 来源:网络整理
导读:考虑以下类别的定义: 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:
考虑以下类别的定义:

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的 ArbitraryGen选择要使用的功能。

首先,为要生成的函数定义一个基类。一般来说,可以生成具有未定义结果(例如除以零)的函数,因此我们将使用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 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))
}

虽然我所展示的只是概括了测试的功能,希望这将给你一个想法如何使用高级类型系统诡计来推广类型。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读