And*_*ski 5 generics polymorphism scala higher-kinded-types scalacheck
我正在尝试为 scala 中的函子编写通用定律,格式可以在 scalacheck 测试中重用于许多函子。定律应该由构造函数 F[_] 和元素类型参数化,比如 A。
理想情况下,我会写这样的东西:
def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
Run Code Online (Sandbox Code Playgroud)
(我使用 true 而不是法体,因为确切的计算对我的问题无关紧要)
然而,我能破解的最好方法是将它包装在一个抽象类中,为生成任意 F[A] 值提供一个隐含的抽象:
abstract class FunctorSpec[A :Arbitrary, F[_]] extends Properties("foo") {
implicit def arbitraryFA :Arbitrary[F[A]]
def functorLaw (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
}
Run Code Online (Sandbox Code Playgroud)
现在这可行,但并不理想。我需要为每个测试实例化我想要运行的类,并且需要在那里提供任意 FA 函数。当然,编译器需要这个函数,但是对于很多类型,它们存在应该执行它的隐式(例如对于 List[Int])。但是编译器将无法猜测这些隐式提供了任意FA,因此我需要自己实现这一点,这是非常重复的。例如:
object IntListFunctorSpec extends FunctorSpec[Int, List] {
def arbitraryFA :Arbitrary[List[Int]] = Arbitrary(arbitrary[List[Int]])
...
}
Run Code Online (Sandbox Code Playgroud)
我认为我不需要告诉 scalacheck 如何构建 int 列表。任何建议如何更优雅地做到这一点?
我尝试了其他关于更高级类型边界的问题,但我无法弄清楚如何使用它们,尽管它们听起来很接近。所以我想我会问。
您的尝试失败的原因是您存在某种不匹配。下列:
def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
Run Code Online (Sandbox Code Playgroud)
只是一个语法糖:
def functorLaw[A, F[_]] (fn :Functor[F])(implicit evidence: Arbitrary[F]) :Prop = forAll { (fa :F[A]) => true }
Run Code Online (Sandbox Code Playgroud)
因此,本质上,问题在于您的方法需要类型的隐式值,Arbitrary[F]其中 F 是高阶类型 ( F[_]),但这没有意义,因为Arbitrary不采用高阶类型:
// T is a first order type, it has the kind *
// Simply put, it is not a type constructor
class Arbitrary[T]
Run Code Online (Sandbox Code Playgroud)
为了让您的代码按原样编译(并且有意义),Arbitrary必须声明如下:
// T is a type constructor, it has the kind * -> *
class Arbitrary[T[_]]
Run Code Online (Sandbox Code Playgroud)
现在介绍如何修复它。在您的情况下,您想要的实际任意值是 type F[A],而不是F(这应该不言而喻,因为它不是具体类型,而是类型构造函数),因此您需要的隐式值是 type Arbitrary[F[A]]:
def functorLaw[A, F[_]] (fn :Functor[F])(implicit arb: Arbitrary[F[A]]) :Prop = forAll { (fa :F[A]) => true }
Run Code Online (Sandbox Code Playgroud)
并且因为F[A]不会出现在类型参数列表中(有Aand F,但没有F[A]),所以不能使用“上下文绑定”语法糖,我们必须使用显式(!)隐式参数列表。