MonadParallel Instance for Rand
  我目前正在ReaderT r (Rand StdGen) a ,我希望并行运行它。  我遇到过Monad Parallel,看起来它会做我想要的。 
  对于ReaderT已经有MonadParallel的实例,但我必须从monad-random为Rand创建我自己的实例。  但是,我不确定我是否做得对。  我并不太熟悉Haskell中的并行编程,但我相信有一种期望,即并行运行计算应该能够提供与正常运行时相同的值。  因为我为Rand的bindM2实例使用split (因此从同一个初始生成器获取一组不同的随机数),但实例并非如此。 
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
虽然我觉得有这种情况可以忽略这个(数字仍然是随机的,对吧?)我也不禁感到我失去了一些东西。 这是好的还是有更好的解决方案?
我主要担心的是这违反了以下保证:
  除了副作用的可能顺序之外,这个函数相当于f ma mb-> do {a <- ma; b <- mb; fab} f ma mb-> do {a <- ma; b <- mb; fab} 
这些会有不同的结果:
let g = mkStdGen 0
    r = evalRand (do x <- getRandom
                     y <- getRandom
                     return (x, y)) g
VS
let g = mkStdGen 0
    r = evalRand (P.bindM2 (x y -> return (x,y)) getRandom getRandom) g
  这确实引发了关于split ,伪随机数和关于纯度的随机数性质的有趣问题。  我很难想象一个你的实例会产生不利影响的情况,但是软件系统的复杂性从来没有让我感到惊讶。  正因为bindM2 ,即使它是随机数字,我也不会违反对bindM2的期望。 
  MonadParallel的bindM2有一个固有的问题。  其文档说: 
  并行执行两个monadic计算;  当他们都完成后,将结果传递给函数。  除了副作用的可能顺序之外,这个函数相当于f ma mb-> do {a <- ma; b <- mb; fab} f ma mb-> do {a <- ma; b <- mb; fab} 
问题在于,在一元计算中(与应用函子不同), 值可能取决于效果 , 也取决于它们的排序。 所以这个要求没有多大意义。 考虑
do
  a <- getCurrentTime -- from Date.Time
  b <- getCurrentTime
  return (a <= b)
  这将始终返回True ,但如果您重新排列效果,它将开始返回False 。  显然通过并行化两个计算,你会得到一个非确定性的结果。 
  所以很难解释bindM2的意图。  我会说我们可以将MonadParallel实例分成两类: 
bindM2总是等于f ma mb-> do {a <- ma; b <- mb; fab} f ma mb-> do {a <- ma; b <- mb; fab}  f ma mb-> do {a <- ma; b <- mb; fab} 。  也就是说,效果的顺序不会改变。  这些实现通常使用par定义,不会更改程序的语义,只能并行运行某些部分。 bindM2 ,因此结合bindM2可以任意地不同于f ma mb-> do {a <- ma; b <- mb; fab} f ma mb-> do {a <- ma; b <- mb; fab}  f ma mb-> do {a <- ma; b <- mb; fab} 。  看来目前唯一的这种实例是IO ,它使用forkIO为其中一个计算生成一个新线程。   因此,无论您是否接受bindM2作为有效实例,都取决于您如何解释文档。  如果你认为(2.)是无效的,那么你的实现也是无效的(你也应该拒绝IO )。  如果你认为(2.)是有效的,那么你就不必担心。 
