Scala 中高级类型的类型约束
Type constraint for higher-kinded type in Scala
我正在尝试为 Scala 中的仿函数编写一个通用法则,其格式我可以在 scalacheck 测试中为许多仿函数重用。法则应该由构造函数 F[_] 和元素类型参数化,比如 A。
理想情况下我会写这样的东西:
def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
(我使用 true 而不是 law body,因为确切的计算对我的问题并不重要)
然而,我能破解的最好方法是将其包装在抽象中 class,为生成任意 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 }
}
现在这可行,但并不理想。我需要为每个测试实例化 class,我想 运行,并且需要在那里提供 arbitraryFA 函数。当然,编译器需要这个函数,但对于许多类型,它们存在隐式应该这样做(例如 List[Int])。然而编译器将无法猜测这些隐式提供任意FA,所以我需要自己实现这个,这是非常重复的。例如:
object IntListFunctorSpec extends FunctorSpec[Int, List] {
def arbitraryFA :Arbitrary[List[Int]] = Arbitrary(arbitrary[List[Int]])
...
}
我认为我不需要告诉 scalacheck 如何构建 int 列表。有什么建议可以更优雅地做到这一点吗?
我尝试了其他关于更高类型边界的问题,但我无法弄清楚如何使用它们,尽管它们听起来很接近。所以我想我会问。
您的尝试失败的原因是您的类型不匹配。
以下:
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[_]
),但这没有意义,因为 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
,但没有F[A]
),"context bound"不能使用语法糖,我们必须使用显式 (!) 隐式参数列表。
我正在尝试为 Scala 中的仿函数编写一个通用法则,其格式我可以在 scalacheck 测试中为许多仿函数重用。法则应该由构造函数 F[_] 和元素类型参数化,比如 A。
理想情况下我会写这样的东西:
def functorLaw[A, F[_] :Arbitrary] (fn :Functor[F]) :Prop = forAll { (fa :F[A]) => true }
(我使用 true 而不是 law body,因为确切的计算对我的问题并不重要)
然而,我能破解的最好方法是将其包装在抽象中 class,为生成任意 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 }
}
现在这可行,但并不理想。我需要为每个测试实例化 class,我想 运行,并且需要在那里提供 arbitraryFA 函数。当然,编译器需要这个函数,但对于许多类型,它们存在隐式应该这样做(例如 List[Int])。然而编译器将无法猜测这些隐式提供任意FA,所以我需要自己实现这个,这是非常重复的。例如:
object IntListFunctorSpec extends FunctorSpec[Int, List] {
def arbitraryFA :Arbitrary[List[Int]] = Arbitrary(arbitrary[List[Int]])
...
}
我认为我不需要告诉 scalacheck 如何构建 int 列表。有什么建议可以更优雅地做到这一点吗?
我尝试了其他关于更高类型边界的问题,但我无法弄清楚如何使用它们,尽管它们听起来很接近。所以我想我会问。
您的尝试失败的原因是您的类型不匹配。 以下:
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[_]
),但这没有意义,因为 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
,但没有F[A]
),"context bound"不能使用语法糖,我们必须使用显式 (!) 隐式参数列表。