packages feed

fused-effects-random-1.1.0.0: src/Control/Carrier/Random/Gen.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | A carrier for "Control.Effect.Random".'Random' implemented using 'R.RandomGen'.
--
-- @since 1.1
module Control.Carrier.Random.Gen
( -- * Random carrier
  runRandom
, evalRandom
, execRandom
, evalRandomSystem
, RandomC(RandomC)
  -- * Random effect
, module Control.Effect.Random
) where

import           Control.Algebra
import           Control.Applicative (Alternative)
import           Control.Carrier.State.Church
import           Control.Effect.Random
import           Control.Monad (MonadPlus)
import qualified Control.Monad.Fail as Fail
import           Control.Monad.Fix
import           Control.Monad.IO.Class (MonadIO(..))
import           Control.Monad.Trans.Class
import qualified System.Random as R (Random(..), RandomGen(..), StdGen, newStdGen)

-- | Run a random computation starting from a given generator.
--
-- @
-- 'runRandom' g ('pure' b) = 'pure' (g, b)
-- @
--
-- @since 1.0
runRandom :: Applicative m => g -> RandomC g m a -> m (g, a)
runRandom g = runState (curry pure) g . runRandomC
{-# INLINE runRandom #-}

-- | Run a random computation starting from a given generator and discarding the final generator.
--
-- @
-- 'evalRandom' g ('pure' b) = 'pure' b
-- @
--
-- @since 1.0
evalRandom :: Applicative m => g -> RandomC g m a -> m a
evalRandom g = evalState g . runRandomC
{-# INLINE evalRandom #-}

-- | Run a random computation starting from a given generator and discarding the final result.
--
-- @
-- 'execRandom' g ('pure' b) = g
-- @
--
-- @since 1.0
execRandom :: Applicative m => g -> RandomC g m a -> m g
execRandom g = execState g . runRandomC
{-# INLINE execRandom #-}

-- | Run a random computation in 'IO', splitting the global standard generator to get a new one for the computation.
--
-- @since 1.1
evalRandomSystem :: MonadIO m => RandomC R.StdGen m a -> m a
evalRandomSystem m = liftIO R.newStdGen >>= flip evalRandom m
{-# INLINE evalRandomSystem #-}

-- | A carrier for 'Random' implemented using 'R.RandomGen'.
--
-- @since 1.0
newtype RandomC g m a = RandomC { runRandomC :: StateC g m a }
  deriving (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus, MonadTrans)

instance (Algebra sig m, R.RandomGen g) => Algebra (Random :+: sig) (RandomC g m) where
  alg hdl sig ctx = RandomC $ case sig of
    L random -> StateC $ \ k g -> case random of
      Uniform      -> let (a,   g') = R.random    g in k g' (a <$ ctx)
      UniformR r   -> let (a,   g') = R.randomR r g in k g' (a <$ ctx)
      Interleave m -> let (g'', g') = R.split     g in runState (const (k g'')) g' (runRandomC (hdl (m <$ ctx)))
    R other  -> alg (runRandomC . hdl) (R other) ctx
  {-# INLINE alg #-}