{-# 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 ) -------------------------------------------------------------------------- -- ** 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