是否可以为 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)
  }