我目前正在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
虽然我觉得有一种情况可以忽略这个(数字仍然是随机的,对吗?)我也不禁感到我错过了什么。这没关系还是有更好的解决方案?
答案 0 :(得分: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
VS
let g = mkStdGen 0
r = evalRand (P.bindM2 (\x y -> return (x,y)) getRandom getRandom) g
这确实引发了关于split
,伪随机数以及随机数在纯度方面的性质的有趣问题。我很难想象你的实例会产生不利影响的情况,但软件系统的复杂性从未让我感到惊讶。因此,我不会违反对bindM2
的期望,即使它是随机数字。
答案 1 :(得分:1)
MonadParallel
的{{1}}存在固有问题。它的文件说:
并行执行两个monadic计算;当它们都完成后,将结果传递给函数。 除了可能的副作用排序外,此功能相当于
bindM2
问题是在monadic计算中(与applicative functors不同)值可能依赖于效果,也取决于它们的排序。所以这个要求没有多大意义。考虑
\f ma mb-> do {a <- ma; b <- mb; f a b}
这将始终返回do
a <- getCurrentTime -- from Date.Time
b <- getCurrentTime
return (a <= b)
,但如果您重新排序效果,它将开始返回True
。
显然,通过并行化这两个计算,你会得到一个非常不确定的结果。
因此很难解释False
的意图。我想我们可以将bindM2
的实例分为两类:
MonadParallel
总是等于bindM2
的人。也就是说,效果的顺序不会改变。这些实现通常使用\f ma mb-> do {a <- ma; b <- mb; f a b}
定义,它不会改变程序的语义,只能并行运行某些部分。par
可以与bindM2
任意不同。似乎目前唯一的此类实例是\f ma mb-> do {a <- ma; b <- mb; f a b}
,它使用IO
为其中一个计算生成一个新线程。因此,您是否接受forkIO
作为有效实例取决于您如何解释文档。如果您认为(2.)无效,那么您的实现也是无效的(您也应拒绝bindM2
)。如果您将(2.)解释为有效,那么您不必担心。