{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
-- | A random number effect, using a pure mersenne twister under
-- the hood. This should be plug-and-play with any application
-- making use of extensible effects.
--
-- Patches, even for the smallest of documentation bugs, are
-- always welcome!
module System.Random.Effect ( Random
, mkRandom
, mkRandomIO
, randomInt
, randomInt64
, randomWord
, randomWord64
, randomDouble
) where
import Data.Int
import Data.Typeable
import Data.Word
import qualified System.Random.Mersenne.Pure64 as SR
import Control.Eff
import Control.Eff.State
import Control.Eff.Lift
-- | A pure mersenne twister pseudo-random number generator.
data Random = Random {-# UNPACK #-} !SR.PureMT
deriving Typeable
-- | Create a random number generator from a 'Word64' seed.
mkRandom :: Word64 -> Random
mkRandom = Random . SR.pureMT
{-# INLINE mkRandom #-}
-- | Create a new random number generator, using the clocktime as the base for
-- the seed. This must be called from a computation with a lifted base effect
-- of 'IO'.
mkRandomIO :: Member (Lift IO) r
=> Eff r Random
mkRandomIO = lift (fmap Random SR.newPureMT)
{-# INLINE mkRandomIO #-}
-- | Runs an effectful random computation, returning the computation's result.
runRandomState :: Random
-> Eff (State Random :> r) w
-> Eff r w
runRandomState seed computation =
fmap snd (runState seed computation)
{-# INLINE runRandomState #-}
-- | A generalized form of generating a random number of the correct type
-- from System.Random.Mersenne.Pure64.
randomF :: Member (State Random) r
=> (SR.PureMT -> (a, SR.PureMT))
-> Eff r a
randomF f = do
(Random old) <- getState
let (val, new) = f old
putState (Random new)
return val
{-# INLINE randomF #-}
-- | Yield a new 'Int' value from the generator. The full 64 bits will be used
-- on a 64 bit machine.
randomInt :: Member (State Random) r => Eff r Int
randomInt = randomF SR.randomInt
{-# INLINE randomInt #-}
-- | Yield a new 'Word' value from the generator.
randomWord :: Member (State Random) r => Eff r Word
randomWord = randomF SR.randomWord
{-# INLINE randomWord #-}
-- | Yield a new 'Int64' value from the generator.
randomInt64 :: Member (State Random) r => Eff r Int64
randomInt64 = randomF SR.randomInt64
{-# INLINE randomInt64 #-}
-- | Yield a new 'Word64' value from the generator.
randomWord64 :: Member (State Random) r => Eff r Word64
randomWord64 = randomF SR.randomWord64
{-# INLINE randomWord64 #-}
-- | Yield a new 53-bit precise 'Double' value from the generator.
randomDouble :: Member (State Random) r => Eff r Double
randomDouble = randomF SR.randomDouble
{-# INLINE randomDouble #-}