{-# LANGUAGE GADTs #-}
-- | Random variables in uniform and exponential distributions, with interleaving.
--
-- @since 1.0
module Control.Effect.Random
( -- * Random effect
  Random(..)
, uniform
, uniformR
, interleave
  -- * Non-uniform distributions
, exponential
  -- * Re-exports
, Algebra
, Has
, run
) where

import           Control.Algebra
import qualified System.Random as R (Random(..))

-- | Uniformly-distributed random variables, with interleaving.
--
-- @since 1.0
data Random m k where
  Uniform    :: R.Random a =>           Random m a
  UniformR   :: R.Random a => (a, a) -> Random m a
  Interleave :: m a                  -> Random m a


-- | Produce a random variable uniformly distributed in a range determined by its type’s 'R.Random' instance. For example:
--
-- * bounded types (instances of 'Bounded', such as 'Char') typically sample all of the constructors.
-- * fractional types, the range is normally the semi-closed interval [0,1).
-- * for 'Integer', the range is (arbitrarily) the range of 'Int'.
--
-- @since 1.1
uniform :: (R.Random a, Has Random sig m) => m a
uniform :: m a
uniform = Random m a -> m a
forall (eff :: (* -> *) -> * -> *) (sig :: (* -> *) -> * -> *)
       (m :: * -> *) a.
(Member eff sig, Algebra sig m) =>
eff m a -> m a
send Random m a
forall a (m :: * -> *). Random a => Random m a
Uniform
{-# INLINE uniform #-}

-- | Produce a random variable uniformly distributed in the given range.
--
-- @
-- 'Data.Ix.inRange' (a, b) '<$>' 'uniformR' (a, b) = 'pure' 'True'
-- @
--
-- @since 1.1
uniformR :: (R.Random a, Has Random sig m) => (a, a) -> m a
uniformR :: (a, a) -> m a
uniformR (a, a)
interval = Random m a -> m a
forall (eff :: (* -> *) -> * -> *) (sig :: (* -> *) -> * -> *)
       (m :: * -> *) a.
(Member eff sig, Algebra sig m) =>
eff m a -> m a
send ((a, a) -> Random m a
forall a (m :: * -> *). Random a => (a, a) -> Random m a
UniformR (a, a)
interval)
{-# INLINE uniformR #-}

-- | Run a computation by splitting the generator, using one half for the passed computation and the other for the continuation.
--
-- @
-- 'interleave' ('pure' a) = 'pure' a
-- @
--
-- @since 1.0
interleave :: Has Random sig m => m a -> m a
interleave :: m a -> m a
interleave m a
m = Random m a -> m a
forall (eff :: (* -> *) -> * -> *) (sig :: (* -> *) -> * -> *)
       (m :: * -> *) a.
(Member eff sig, Algebra sig m) =>
eff m a -> m a
send (m a -> Random m a
forall (m :: * -> *) a. m a -> Random m a
Interleave m a
m)
{-# INLINE interleave #-}


-- * Non-uniform distributions

-- | Produce a random variable in an expnoential distribution with the given scale.
--
-- @since 1.1
exponential :: (R.Random a, Floating a, Has Random sig m) => a -> m a
exponential :: a -> m a
exponential a
a = do
  a
x <- m a
forall a (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Random a, Has Random sig m) =>
m a
uniform
  a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> m a) -> a -> m a
forall a b. (a -> b) -> a -> b
$! -a -> a
forall a. Floating a => a -> a
log a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
a
{-# INLINE exponential #-}