是否可以为 ScalaCheck 的 Gen 类型编写分布式实例?
Is it possible to write a Distributive Instance for ScalaCheck's Gen type?
我正在尝试为 ScalaCheck 的 Gen
类型编写一个 Monad Transformer 实例。
即:如下所示的类型,可以用作 Monad,前提是底层函子 F
是 Monad。
case class GeneratorT[F[_], A](generator: Gen[F[A]])
object GeneratorT {
implicit def monadForGeneratorT[F[_]: Monad]: Monad[GeneratorT[F, *]] = new Monad[GeneratorT[F, *]] {
...
}
}
在写这篇文章时,我意识到如果我能够为 Gen
编写一个 Distributive
实例将会很有用,因为那样我就可以编写 flatMap
对于 GeneratorT
以下(有点复杂)的方式:
override def flatMap[A, B](ga: GeneratorT[F, A])(fun: A => GeneratorT[F, B]): GeneratorT[F, B] = {
GeneratorT[F, B](ga.generator.flatMap(fa => fa.map(a => fun(a).generator).distribute(identity).map(_.flatten)))
}
本能地,我觉得我应该能够为 Gen
编写一个 Distributive
实例,因为 Gen
或多或少是 从某些配置和种子到值的函数,函数是分配的。
话虽如此,我没能找到任何人这样做的例子,而且我正在努力编写它,因为 ScalaCheck 没有公开 Gen
的内部结构。
这可能吗?
我想我明白为什么这不太可能;这是因为 Gen
支持过滤,因此,有可能以没有有效值的 Gen
结束。
我想 Gen
是一个函数 (Properties, Seed) => A
,但实际上,这应该更像 (Properties, Seed) => Option[A]
。
第一种分发,第二种不分发。
例如,如果 Gen
可能失败,则 IO[Gen[A]]
无法转换为 Gen[IO[A]]
,因为如果不评估 [=19],则无法知道失败=].
如果您假装任何生成器在经过足够多的评估后都会产生一个值,并且您愿意在实际情况并非如此时遇到异常,则可以像这样实现 Distributive
:
implicit val distributiveForGen: Distributive[Gen] = new Distributive[Gen] {
override def distribute[G[_]: Functor, A, B](ga: G[A])(f: A => Gen[B]): Gen[G[B]] =
Gen.parameterized(params => ga.map(a => f(a).pureApply(params, params.initialSeed.getOrElse(Seed(0)))))
override def map[A, B](fa: Gen[A])(f: A => B): Gen[B] = fa.map(f)
}
我正在尝试为 ScalaCheck 的 Gen
类型编写一个 Monad Transformer 实例。
即:如下所示的类型,可以用作 Monad,前提是底层函子 F
是 Monad。
case class GeneratorT[F[_], A](generator: Gen[F[A]])
object GeneratorT {
implicit def monadForGeneratorT[F[_]: Monad]: Monad[GeneratorT[F, *]] = new Monad[GeneratorT[F, *]] {
...
}
}
在写这篇文章时,我意识到如果我能够为 Gen
编写一个 Distributive
实例将会很有用,因为那样我就可以编写 flatMap
对于 GeneratorT
以下(有点复杂)的方式:
override def flatMap[A, B](ga: GeneratorT[F, A])(fun: A => GeneratorT[F, B]): GeneratorT[F, B] = {
GeneratorT[F, B](ga.generator.flatMap(fa => fa.map(a => fun(a).generator).distribute(identity).map(_.flatten)))
}
本能地,我觉得我应该能够为 Gen
编写一个 Distributive
实例,因为 Gen
或多或少是 从某些配置和种子到值的函数,函数是分配的。
话虽如此,我没能找到任何人这样做的例子,而且我正在努力编写它,因为 ScalaCheck 没有公开 Gen
的内部结构。
这可能吗?
我想我明白为什么这不太可能;这是因为 Gen
支持过滤,因此,有可能以没有有效值的 Gen
结束。
我想 Gen
是一个函数 (Properties, Seed) => A
,但实际上,这应该更像 (Properties, Seed) => Option[A]
。
第一种分发,第二种不分发。
例如,如果 Gen
可能失败,则 IO[Gen[A]]
无法转换为 Gen[IO[A]]
,因为如果不评估 [=19],则无法知道失败=].
如果您假装任何生成器在经过足够多的评估后都会产生一个值,并且您愿意在实际情况并非如此时遇到异常,则可以像这样实现 Distributive
:
implicit val distributiveForGen: Distributive[Gen] = new Distributive[Gen] {
override def distribute[G[_]: Functor, A, B](ga: G[A])(f: A => Gen[B]): Gen[G[B]] =
Gen.parameterized(params => ga.map(a => f(a).pureApply(params, params.initialSeed.getOrElse(Seed(0)))))
override def map[A, B](fa: Gen[A])(f: A => B): Gen[B] = fa.map(f)
}