I'm currently working with computations in ReaderT r (Rand StdGen) a
which I would like to run in parallel. I've come across Monad Parallel which seems like it will do what I want.
There is already an instance of MonadParallel for ReaderT
but I had to create my own for Rand
from monad-random. However, I'm not sure I've done it right. I'm not too familiar with parallel programming in Haskell, but I believe that there is an expectation that running compuations in parrallel should give the same value as when they're run normally. Because my instance of bindM2 for Rand uses split
(and therefore gets a different set of random numbers from the same initial generator) this isn't the case for my instance.
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
While I feel that there is a case for ignoring this (the numbers are still random, right?) I also can't help feeling that I'm missing something. Is this okay or is there a better solution?
My main concern is that this violates the guarantee that:
Apart from the possible ordering of side effects, this function is equivalent to
\f ma mb-> do {a <- ma; b <- mb; f a b}
These will have different results:
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
This does raise interesting questions about split
, pseudorandom numbers, and the nature of random numbers in regards to purity. It's hard for me to imagine a case where your instance would have adverse affects, but the complexity of software systems has never stopped surprising me. And because of that, I wouldn't violate an expectation about bindM2
, even if it is with random numbers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With