Personal tools

New monads/MonadRandomSplittable

From HaskellWiki

Jump to: navigation, search

When using New monads/MonadRandom, one may also want to use a
equivalent of
class (MonadRandom m) => MonadRandomSplittable m where
    splitRandom :: m a -> m a
instance (Monad m, RandomGen g) => MonadRandomSplittable (RandomT g m) where
    splitRandom ma  = (RandomT . liftState) split >>= lift . evalRandomT ma

MonadRandomSplittable can then be derived for Rand by GHC:

newtype Rand g a = Rand { unRand :: RandomT g Identity a }
    deriving (Functor, Monad, MonadRandom, MonadRandomSplittable)

Some potentially useful functions

splitRandoms        :: MonadRandomSplittable m => [m a] -> m [a]
splitRandoms []     = splitRandom $ return []
splitRandoms (x:xs) = splitRandom $ liftM2 (:) x (splitRandoms xs)
getRandoms      :: (MonadRandomSplittable m, Random a) => m [a]
getRandoms      = liftM2 (:) getRandom (splitRandom getRandoms)
getRandomRs     :: (MonadRandomSplittable m, Random a) => (a, a) -> m [a]
getRandomRs b   = liftM2 (:) (getRandomR b) (splitRandom (getRandomRs b))

1 Example of usage

test   :: Rand StdGen [Bool] -> (Int, [Bool], Int)
test ma = evalRand (liftM3 (,,) (getRandomR (0,99)) ma (getRandomR (0,99)))
                (mkStdGen 0)


*MonadRandom> test (replicateM 0 getRandom)
*MonadRandom> test (replicateM 2 getRandom)
*MonadRandom> test (splitRandom $ replicateM 0 getRandom)
*MonadRandom> test (splitRandom $ replicateM 2 getRandom)
*MonadRandom> case test undefined of (a,_,c) -> (a,c)
*** Exception: Prelude.undefined
*MonadRandom> case test (splitRandom undefined) of (a,_,c) -> (a,c)

2 Laws

It is not clear to me exactly what laws
should satisfy, besides monadic variations of the "split laws" from the Haskell Library Report For all terminating
, it should hold that
  liftM3 (\a _ c -> (a,c)) getRandom (splitRandom ma) getRandom


  liftM3 (\a _ c -> (a,c)) getRandom (splitRandom mb) getRandom

return the same pair.

For monad transformers, it would also be nice if

splitRandom undefined === splitRandom (return ()) >> lift undefined

For example,

>runIdentity $ runRandomT (splitRandom (return ()) >> lift undefined >> return ()) (mkStdGen 0)
((),40014 2147483398)
>runIdentity $ runRandomT (splitRandom undefined >> return ()) (mkStdGen 0)
((),40014 2147483398)


>runRandomT (splitRandom (return ()) >> lift undefined >> return ()) (mkStdGen 0)
*** Exception: Prelude.undefined
>runRandomT (splitRandom undefined >> return ()) (mkStdGen 0)
*** Exception: Prelude.undefined
I have no idea how to express this idea for monads that aren't transformers though. But for
it means that:
>runRand (splitRandom undefined >> return ()) (mkStdGen 0)
((),40014 2147483398)

3 Why?

replicateM 100 (splitRandom expensiveAction)
There are no RNG-dependencies between the different expensiveActions, so they may be computed in parallel.

The following constructs a tree of infinite depth and width:

import Data.Tree
import Data.List
makeRandomTree  = liftM2 Node (getRandomR ('a','z')) (splitRandoms $ repeat makeRandomTree)

By removing the RNG-dependencies, infinite random data structures can be constructed lazily.

And for completeness the non-monadic version:

randomTree g    = Node a (map randomTree gs)
        (a, g') = randomR ('a','z') g
        gs      = unfoldr (Just . split) g'

Note that the monadic version does more split operations, so yields different results.