7

我目前正在处理ReaderT r (Rand StdGen) a我想并行运行的计算。我遇到了Monad Parallel,它似乎会做我想做的事。

已经有一个 MonadParallel for 的实例,但我必须从monad-randomReaderT创建自己的 for 。但是,我不确定我做对了。我对 Haskell 中的并行编程不太熟悉,但我相信在并行中运行计算应该给出与正常运行时相同的值。因为我为 Rand 使用的 bindM2 实例(因此从同一个初始生成器获得了一组不同的随机数),所以我的实例并非如此。Randsplit

instance P.MonadParallel (Rand StdGen) where
    bindM2 f ma mb = do
        split1 <- getSplit
        split2 <- getSplit
        let a = evalRand ma split1
        let b = evalRand mb split2
        a `par` b `pseq` f a b

虽然我觉得有理由忽略这一点(数字仍然是随机的,对吗?)我也忍不住觉得我错过了一些东西。这没问题还是有更好的解决方案?

4

2 回答 2

2

我主要担心的是这违反了以下保证:

除了可能的副作用排序之外,这个函数等价于\f ma mb-> do {a <- ma; b <- mb; f a b}

这些会有不同的结果:

let g = mkStdGen 0
    r = evalRand (do x <- getRandom
                     y <- getRandom
                     return (x, y)) g

对比

let g = mkStdGen 0
    r = evalRand (P.bindM2 (\x y -> return (x,y)) getRandom getRandom) g

这确实引发了一些有趣的问题split,例如伪随机数以及随机数在纯度方面的性质。我很难想象您的实例会产生不利影响的情况,但是软件系统的复杂性从未停止让我感到惊讶。bindM2因此,即使是随机数,我也不会违反对 的期望。

于 2013-01-30T08:28:40.120 回答
1

MonadParallel's中存在一个固有的问题bindM2。它的文档说:

并行执行两个一元计算;当它们都完成后,将结果传递给函数。除了可能的副作用排序之外,这个函数等价于\f ma mb-> do {a <- ma; b <- mb; f a b}

问题在于,在单子计算中(与应用函子不同) ,值可能取决于 effects以及它们的顺序。所以这个要求没有多大意义。考虑

do
  a <- getCurrentTime -- from Date.Time
  b <- getCurrentTime
  return (a <= b)

这总是返回True,但如果你重新排序效果,它将开始返回False。显然,通过并行化这两个计算,你会得到一个非常不确定的结果。

所以很难解读其意图bindM2。我想说我们可以将实例MonadParallel分为两类:

  1. 那些真正具有确定性且bindM2始终等于 的\f ma mb-> do {a <- ma; b <- mb; f a b}。也就是说,效果的顺序不会改变。这些实现通常使用 定义par,它不会改变程序的语义,只会并行运行一些部分。
  2. 那些确实依赖于效果排序的,因此bindM2可以与 任意不同\f ma mb-> do {a <- ma; b <- mb; f a b}。似乎目前唯一这样的实例是IO,它用于forkIO为其中一个计算生成一个新线程。

因此,您是否接受您bindM2作为有效实例取决于您如何解释文档。如果您认为 (2.) 无效,那么您的实现也是无效的(您也应该拒绝IO)。如果您将 (2.) 解释为有效,那么您不必担心。

于 2013-01-30T13:04:24.653 回答