fused-effects-0.5.0.1: src/Control/Effect/Random.hs
{-# LANGUAGE DeriveFunctor, ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, ScopedTypeVariables, StandaloneDeriving, TypeOperators, UndecidableInstances #-}
module Control.Effect.Random
( -- * Random effect
Random(..)
-- * Random carrier
, runRandom
, evalRandom
, execRandom
, evalRandomIO
, RandomC(..)
-- * Re-exports
, Carrier
, Member
, MonadRandom(..)
, MonadInterleave(..)
, run
) where
import Control.Applicative (Alternative(..))
import Control.Effect.Carrier
import Control.Effect.State
import Control.Monad (MonadPlus(..))
import qualified Control.Monad.Fail as Fail
import Control.Monad.Fix
import Control.Monad.Random.Class (MonadInterleave(..), MonadRandom(..))
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Trans.Class
import qualified System.Random as R (Random(..), RandomGen(..), StdGen, newStdGen)
data Random m k
= forall a . R.Random a => Random (a -> m k)
| forall a . R.Random a => RandomR (a, a) (a -> m k)
| forall a . Interleave (m a) (a -> m k)
deriving instance Functor m => Functor (Random m)
instance HFunctor Random where
hmap f (Random k) = Random (f . k)
hmap f (RandomR r k) = RandomR r (f . k)
hmap f (Interleave m k) = Interleave (f m) (f . k)
{-# INLINE hmap #-}
instance Effect Random where
handle state handler (Random k) = Random (handler . (<$ state) . k)
handle state handler (RandomR r k) = RandomR r (handler . (<$ state) . k)
handle state handler (Interleave m k) = Interleave (handler (m <$ state)) (handler . fmap k)
-- | Run a random computation starting from a given generator.
--
-- prop> run (runRandom (PureGen a) (pure b)) === (PureGen a, b)
runRandom :: g -> RandomC g m a -> m (g, a)
runRandom g = runState g . runRandomC
-- | Run a random computation starting from a given generator and discarding the final generator.
--
-- prop> run (evalRandom (PureGen a) (pure b)) === b
evalRandom :: Functor m => g -> RandomC g m a -> m a
evalRandom g = fmap snd . runRandom g
-- | Run a random computation starting from a given generator and discarding the final result.
--
-- prop> run (execRandom (PureGen a) (pure b)) === PureGen a
execRandom :: Functor m => g -> RandomC g m a -> m g
execRandom g = fmap fst . runRandom g
-- | Run a random computation in 'IO', splitting the global standard generator to get a new one for the computation.
evalRandomIO :: MonadIO m => RandomC R.StdGen m a -> m a
evalRandomIO m = liftIO R.newStdGen >>= flip evalRandom m
newtype RandomC g m a = RandomC { runRandomC :: StateC g m a }
deriving (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus, MonadTrans)
instance (Carrier sig m, Effect sig, R.RandomGen g) => MonadRandom (RandomC g m) where
getRandom = RandomC $ do
(a, g') <- gets R.random
a <$ put (g' :: g)
{-# INLINE getRandom #-}
getRandomR r = RandomC $ do
(a, g') <- gets (R.randomR r)
a <$ put (g' :: g)
{-# INLINE getRandomR #-}
getRandomRs interval = (:) <$> getRandomR interval <*> getRandomRs interval
{-# INLINE getRandomRs #-}
getRandoms = (:) <$> getRandom <*> getRandoms
{-# INLINE getRandoms #-}
instance (Carrier sig m, Effect sig, R.RandomGen g) => MonadInterleave (RandomC g m) where
interleave m = RandomC $ do
(g1, g2) <- gets R.split
put (g1 :: g)
a <- runRandomC m
a <$ put g2
{-# INLINE interleave #-}
instance (Carrier sig m, Effect sig, R.RandomGen g) => Carrier (Random :+: sig) (RandomC g m) where
eff (L (Random k)) = getRandom >>= k
eff (L (RandomR r k)) = getRandomR r >>= k
eff (L (Interleave m k)) = interleave m >>= k
eff (R other) = RandomC (eff (R (handleCoercible other)))
{-# INLINE eff #-}
-- $setup
-- >>> :seti -XFlexibleContexts
-- >>> import System.Random
-- >>> import Test.QuickCheck
-- >>> import Control.Effect.Pure
-- >>> import Control.Effect.NonDet
-- >>> newtype PureGen = PureGen Int deriving (Eq, Show)
-- >>> instance RandomGen PureGen where next (PureGen i) = (i, PureGen i) ; split g = (g, g)