random-eff-0.1.0.1: Control/Eff/Random.hs
{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor #-}
{-# LANGUAGE ExistentialQuantification, FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses, NoMonomorphismRestriction #-}
{-# LANGUAGE ScopedTypeVariables, StandaloneDeriving, TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Control.Eff.Random (module System.Random,
Rand, Generator(..),
-- * Execution
runRand, evalRand, evalRandIO,
-- * Generator functions
getRandom, getRandomR, getRandoms, getRandomRs,
fromList, uniform,
-- * Misc
getSplit) where
import Control.Arrow (first)
import Control.Arrow (second)
import Control.Eff
import Control.Eff.Lift (Lift, lift)
import Control.Monad (liftM)
import Data.Typeable (Typeable (..), mkTyCon3, mkTyConApp)
import System.Random
-- | Wrapper Type for 'RandomGen' types
--
-- Since 0.1.0.0
data Generator = forall g. RandomGen g => Generator g
#if MIN_VERSION_base(4,7,0)
deriving (Typeable)
#else
instance Typeable Generator where
typeOf _ = mkTyConApp gen []
where
gen = mkTyCon3 "random-eff" "Control.Eff.Random" "Generator"
#endif
-- | This behaves exactly as same as the original, un-quantified instance.
--
-- Since 0.1.0.0
instance RandomGen Generator where
next (Generator g) = second Generator $ next g
genRange (Generator g) = genRange g
split (Generator g) =
let (h, h') = split g
in (Generator h, Generator h')
-- | Random number generator
--
-- Since 0.1.0.0
data Rand a = Rand (Generator -> (a, Generator))
deriving (Typeable)
instance Functor Rand where
fmap g (Rand f) = Rand (first g . f)
-- | Return a randomly-selected value of type a. See 'random' for details.
--
-- Since 0.1.0.0
getRandom :: forall a r. (Typeable a, Random a, Member Rand r) => Eff r a
getRandom = send $ \k ->
inj $ Rand (\(Generator g) -> let (a :: a, g') = random g
in (k a, Generator g'))
-- | Return an infinite stream of random values of type a. See 'randoms' for details.
--
-- Since 0.1.0.0
getRandoms :: (Random a, Typeable a, Member Rand r) => Eff r [a]
getRandoms = send $ \k ->
inj $ Rand (\(Generator g) -> let (g', g'') = split g
in (k (randoms g'), Generator g''))
-- | Return a randomly-selected value of type a in the range @(lo,hi)@. See 'randomR' for details.
--
-- Since 0.1.0.0
getRandomR :: (Typeable a, Random a, Member Rand r) => (a, a) -> Eff r a
getRandomR bd = send $ \k ->
inj $ Rand (\(Generator g) -> let (a, g') = randomR bd g
in (k a, Generator g'))
-- | Return an infinite stream of randomly-selected value of type a in the range @(lo,hi)@. See 'randomRs' for details.
--
-- Since 0.1.0.0
getRandomRs :: (Typeable a, Random a, Member Rand r) => (a, a) -> Eff r [a]
getRandomRs bd = send $ \k ->
inj $ Rand (\(Generator g) -> let (g', g'') = split g in (k (randomRs bd g'), Generator g''))
-- | Sample a random value from a weighted list. The total weight of all elements must not be 0.
--
-- Since 0.1.0.0
fromList :: Member Rand r => [(a, Rational)] -> Eff r a
fromList [] = error "Eff.Random.fromList called with empty list"
fromList [(x, _)] = return x
fromList xs = do
let s = fromRational $ sum $ map snd xs :: Double
cs = scanl1 (\(_,q) (y,s') -> (y, s'+q)) xs
p <- liftM toRational (getRandomR (0.0,s))
return . fst . head $ dropWhile (\(_,q) -> q < p) cs
-- | Sample a value from a uniform distribution of a list of elements.
--
-- Since 0.1.0.0
uniform :: Member Rand r => [a] -> Eff r a
uniform xs = fromList $ map (flip (,) 1) xs
-- | Split the internal generator. This returns the second result of 'split'
-- and set the new internal generator to the first one.
--
-- Since 0.1.0.0
getSplit :: (Member Rand r) => Eff r Generator
getSplit = send $ \k ->
inj $ Rand (\(Generator g) -> let (g', g'') = split g in (k (Generator g''), Generator g'))
-- | Run a computation with random numbers
--
-- Since 0.1.0.0
runRand :: RandomGen g
=> g -- ^ initial internal random generator
-> Eff (Rand :> r) w -- ^ Effect using random numbers
-> Eff r (w, Generator)
-- ^ Effect containing return value and final random number generator.
-- The generator is returned as existential type due to the limitation
-- of the current implementation, but it's guaranteed to work exactly
-- as same as the original given generator type.
runRand g0 = loop (Generator g0) . admin
where
loop g (Val x) = return (x, g)
loop g (E u) = handleRelay u (loop g) $ \(Rand f) ->
let (a, g') = f g
in loop g' a
-- | Run a computation with random numbers, discarding the final generator.
--
-- Since 0.1.0.0
evalRand :: RandomGen g => g -> Eff (Rand :> r) w -> Eff r w
evalRand g = liftM fst . runRand g
-- | Run a computation with random numbers, using 'newStdGen' as its initial generator.
--
-- Since 0.1.0.0
evalRandIO :: SetMember Lift (Lift IO) r => Eff (Rand :> r) w -> Eff r w
evalRandIO eff = do
g <- lift newStdGen
evalRand g eff