在聚合 monad 上实现 flatMap

Implementing flatMap on aggregate monad

我正在寻求实现一个版本的生成器组合器(例如类似于 ScalaCheck 或 Haskell 的 QuickCheck 中的组合器),其中生成器包含 Rand 的一个实例,一个表示概率分布的单子(取自 breeze 库)。由于是 monad,Rand 实现了 map 和 flatMap。通常,我也想将 Gen 实现为 monad。如下图,Gen的map实现很简单:

// Rand is from the breeze library
trait Rand[T] {
    def map[U](f: T => U): Rand[U]
    def flatMap[U](f: T => Rand[U]): Rand[U]
}

case class Gen[T](dist: Rand[T]) {
  def map[U](f: T => U): Gen[U] = Gen(dist.map { f })

  def flatMap[U](f: T => Gen[U]): Gen[U] = {
    // How to implement this?
  }
}

但是,我不清楚 flatMap 应该如何实现。这很容易实现,还是(例如)需要通过某种中间数据类型进行一定程度的间接访问?

可能的实施方式是

def flatMap[U](f: T => Gen[U]): Gen[U] = 
  Gen (dist.flatMap {f(_).dist})

Gen 的预期语义是否类似于以下内容?

val UnfairCoin = Gen(Rand(Heads -> 0.9, Tails -> 0.1))
val UnfairDieHi = Gen(Rand(1 -> 0.0, 2 -> 0.0, 3 -> 0.0, 4 -> 0.0, 5 -> 0.5. 6 -> 0.5))
val UnfairDieLo = Gen(Rand(1 -> 0.25, 2 -> 0.25, 3 -> 0.25, 4 -> 0.25, 5 -> 0.0, 6 -> 0.0))

def headsHiTailsLo(coinFlip: CoinFlip) = coinFlip match {
  case Heads => UnfairDieHi
  case Tails => UnfairDieLo
}

val flipAndRoll = UnfairCoin flatMap { headsHighTailsLo _ }

其中:

flipAndRoll == Gen(1 -> 0.025, 2 -> 0.025, 3 -> 0.025, 4 -> 0.025, 5 -> 0.45, 5 -> 0.45)

如果这是正确的,那么 Gen.flatMap 的输出将需要生成两个随机样本,每个样本来自每个分布,或者 Rand 必须提供一种计算联合分布并将其投影的方法至 U.

我快速浏览了 Rand.flatMap, and it appears that it generates a Rand that does the two consecutive random samples, although it's not entirely clear. If that's right, then 的文档。

如果这两个随机变量不是独立的,那么您可能需要做一些工作。