我正在尝试为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, *]] {
...
}
}
[在编写此代码时,我意识到如果能够为Distributive
编写Gen
实例,那将很有用,因为这样我就可以在下面为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)))
}
[本能地,我觉得我应该能够为Distributive
编写Gen
实例,因为Gen
是或多或少只是来自某些配置的函数,以及将种子添加到值上,以及功能是分布式的。
话虽如此,但我没有找到任何这样做的例子,并且我正在努力编写它,因为ScalaCheck不会公开Gen
的内部。
这可能吗?
我想我理解为什么这不太可能;这是因为Gen
支持过滤,因此,可能会以没有有效值的Gen
结尾。我本来以为Gen
是函数(Properties, Seed) => A
,但实际上,它应该更像(Properties, Seed) => Option[A]
。第一种分布,第二种不分布。例如,如果IO[Gen[A]]
可能失败,则无法将Gen[IO[A]]
转换为Gen
,因为如果不评估IO
,就无法了解故障。
[如果您假装任何Generator如果评估足够的次数都将产生一个值,并且您愿意在这种情况下遇到异常,则可以像这样隐含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)
}