MonadRandomLazy-0.1: Control/Monad/LazyRandom.hs
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS -Wall -Werror #-}
{- |
Portability : non-portable (multi-parameter type classes, undecidable instances)
License : BSD3
A lazy monad for random-number generation. This monad allows, for example,
computation of infinite random lists.
This monad respects the interface defined by
'Control.Monad.Random.Class.MonadRandom'.
A monadic computation is one that consumes random values.
The bind operation works like the 'Gen' monad in 'Test.QuickCheck':
it does not thread the random seed; instead it *splits* the random seed.
-}
module Control.Monad.LazyRandom
( module System.Random
, module Control.Monad.Random.Class
, evalRand, runRand, evalRandIO
, Rand
)
where
import Control.Monad.Random.Class
--------------------------------------------------------------------------
-- imports
import System.Random
( Random(..)
, RandomGen(..)
, StdGen, mkStdGen, getStdRandom
)
import Control.Monad
( ap
)
import Control.Applicative
( Applicative(..)
)
--------------------------------------------------------------------------
-- ** Based on quickcheck generator type
newtype Rand g a = MkRand (g -> a)
-- | A value of type 'Rand g a' is a monadic computation which, when
-- run, consumes random values from an applicative random-number generator
-- of type 'g' and produces a result of type 'a'.
instance Functor (Rand g) where
fmap f (MkRand h) = MkRand (f . h)
instance RandomGen g => Applicative (Rand g) where
pure = return
(<*>) = ap
instance RandomGen g => Monad (Rand g) where
return x = MkRand (const x)
MkRand m >>= k =
MkRand (\r ->
let (r1,r2) = split r
MkRand m' = k (m r1)
in m' r2
)
-- | Evaluate a random computation using the generator @g@. The
-- new @g@ is discarded.
evalRand :: (RandomGen g) => Rand g a -> g -> a
evalRand (MkRand f) g = f g
-- | Run a random computation using the generator @g@, returning the result
-- and a new generator.
runRand :: (RandomGen g) => Rand g a -> g -> (a, g)
runRand (MkRand f) g = (f g1, g2)
where (g1, g2) = split g
-- | Evaluate a random computation in the IO monad, using the random number
-- generator supplied by 'System.Random.getStdRandom'.
evalRandIO :: Rand StdGen a -> IO a
evalRandIO m = getStdRandom (runRand m)
instance (RandomGen g) => MonadRandom (Rand g) where
getRandom = MkRand $ fst . random
getRandoms = MkRand $ randoms
getRandomR range = MkRand $ fst . randomR range
getRandomRs range = MkRand $ randomRs range
instance (RandomGen g) => MonadSplit g (Rand g) where
getSplit = MkRand id -- I think this has to be right ---NR