ScalaCheck:生成任意类型的任意函数

Mar*_*iro 5 scala scalatest scalacheck

我已经实现了以下功能:

/**
  * Returns a function h , which is the composition of the functions f and g.
  */
def compose[A, B, C](g: B => C, f: A => B): A => C = f.andThen(g)
Run Code Online (Sandbox Code Playgroud)

我正在尝试用ScalaCheck测试它。我可以生成以下测试,这些测试可以编译并通过:

import org.scalatest.prop.PropertyChecks
import org.scalatest.{FlatSpec, Matchers}

class ComposeSpec extends FlatSpec with Matchers with PropertyChecks {

  "Compose" should "return a function h , which is the composition of the 
functions f and g" in {

    forAll { (a: Int, g: Int => Int, f: Int => Int) =>
      compose(g, f)(a) should be(g(f(a)))
    }

    forAll { (a: String, g: Double => Int, f: String => Double) =>
      compose(g, f)(a) should be(g(f(a)))
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,正如您所看到的,我正在生成具有定义类型的任意函数,并将参数的类型a与函数的输入类型进行匹配f。我想做的是这样的:

forAll { (a: A, g: B => C, f: A => B) =>
  compose(g, f)(a) should be(g(f(a)))
}
Run Code Online (Sandbox Code Playgroud)

但我不知道它的语法,也不知道它是否可能。你可以帮帮我吗?

Joe*_*e K 5

scalatest 网站有这样的说法forAll

需要提供隐式Arbitrary生成器和对象 该方法会将每行数据传递给每个参数类型。ScalaCheck在其 伴生对象中为常见类型(例如、、等)提供了许多隐式生成器。只要您使用 ScalaCheck 已经提供隐式生成器的类型,您就不必担心它们。对象也是如此,由 ScalaCheck 的伴生对象提供。大多数情况下,您可以简单地将属性函数传递给,编译器将获取 ScalaCheck 提供的隐式值。ShrinkforAllArbitraryIntStringList[Float]org.scalacheck.ArbitraryArbitraryShrinkorg.scalacheck.ShrinkforAll

因此,不幸的是,您不能用于检查每种可能的类型,因为每种可能的类型forAll都没有隐式的Arbitraryand对象。Shrink生成任何类型的任意对象似乎几乎是不可能的。

你能做的最好的事情就是:

def checkComposeForAll[A : Arbitrary : Shrink, B : Arbitrary : Shrink, C : Arbitrary : Shrink]() = {
  forAll { (a: A, g: B => C, f: A => B) =>
    compose(g, f)(a) should be(g(f(a)))
  }
}

checkComposeForAll[Int, Int, Int]()
checkComposeForAll[String, String, String]()
checkComposeForAll[List[Int], Double, Int]()
// ... etc, check a bunch of types ...
Run Code Online (Sandbox Code Playgroud)