在Scala

时间:2015-09-15 14:31:55

标签: scala generics polymorphism scalacheck higher-kinded-types

我正在尝试在scala中为Functors编写一个通用法则,这种格式可以在scalacheck测试中重复用于许多仿函数。法律应该由构造函数F [_]和元素类型参数化,比如A.

理想情况下,我会这样写:

def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }

(我使用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 } 

}

现在这种方法有效,但并不理想。我需要为每个测试实例化我想要运行的类,并且需要在那里提供任意的ARM函数。当然,编译器需要这个函数,但对于许多类型,它们存在应该这样做的含义(例如对于List [Int])。然而编译器将无法猜测这些隐式提供任意FA,所以我需要自己实现,这是非常重复的。例如:

object IntListFunctorSpec extends FunctorSpec[Int, List] {
  def arbitraryFA :Arbitrary[List[Int]] = Arbitrary(arbitrary[List[Int]])

  ...
}

我认为我不应该告诉scalacheck如何构建int列表。有什么建议如何更优雅地做到这一点?

我尝试了更高级别的类型边界的其他问题,我无法弄清楚如何使用它们,即使它们听起来很接近。所以我想我会问。

1 个答案:

答案 0 :(得分:3)

你的尝试不起作用的原因是因为你有一种不匹配。 以下内容:

def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }

只是一个语法糖:

def functorLaw[A, F[_]] (fn :Functor[F])(implicit evidence: Arbitrary[F]) :Prop = forAll { (fa :F[A]) => true }

所以从本质上讲,问题是你的方法需要一个类型为Arbitrary[F]的隐式值,其中F是一个高阶类型(F[_]),但这没有意义,因为{{1不采用更高阶的类型:

Arbitrary

为了让您的代码按原样编译(并且有意义),// T is a first order type, it has the kind * // Simply put, it is not a type constructor class Arbitrary[T] 必须声明如下:

Arbitrary

现在就如何解决它。 在您的情况下,您想要的实际任意值是// T is a type constructor, it has the kind * -> * class Arbitrary[T[_]] 类型,而不是F[A](这应该不言而喻,因为它不是具体类型,而是类型构造函数),因此您需要隐式属于F类型:

Arbitrary[F[A]]

并且因为def functorLaw[A, F[_]] (fn :Functor[F])(implicit arb: Arbitrary[F[A]]) :Prop = forAll { (fa :F[A]) => true } 未出现在类型参数列表中(有F[A]A,而不是F),“上下文绑定”语法糖可以不使用,我们必须使用显式(!)隐式参数列表。