{-# 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 :: forall a (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Random a, Has Random sig m) =>
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 :: forall a (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Random a, Has Random sig m) =>
(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 :: forall (sig :: (* -> *) -> * -> *) (m :: * -> *) a.
Has Random sig m =>
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 :: forall a (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Random a, Floating a, Has Random sig m) =>
a -> m a
exponential a
a = do
  x <- m a
forall a (sig :: (* -> *) -> * -> *) (m :: * -> *).
(Random a, Has Random sig m) =>
m a
uniform
  pure $! -log x / a
{-# INLINE exponential #-}