{-# LANGUAGE
        MultiParamTypeClasses,
        FlexibleInstances, FlexibleContexts, 
        IncoherentInstances
  #-}

{-# OPTIONS_GHC -fno-warn-simplifiable-class-constraints #-}

module Data.Random.Sample where

import Control.Monad.State 
import Data.Random.Distribution
import Data.Random.Lift
import Data.Random.RVar
import Data.Random.Source
import Data.Random.Source.Std

-- |A typeclass allowing 'Distribution's and 'RVar's to be sampled.  Both may
-- also be sampled via 'runRVar' or 'runRVarT', but I find it psychologically
-- pleasing to be able to sample both using this function, as they are two
-- separate abstractions for one base concept: a random variable.
class Sampleable d m t where
    -- |Directly sample from a distribution or random variable, using the given source of entropy.
    sampleFrom :: RandomSource m s => s -> d t -> m t

instance Distribution d t => Sampleable d m t where
    sampleFrom :: s -> d t -> m t
sampleFrom src :: s
src d :: d t
d = RVarT Identity t -> s -> m t
forall (n :: * -> *) (m :: * -> *) s a.
(Lift n m, RandomSource m s) =>
RVarT n a -> s -> m a
runRVarT (d t -> RVarT Identity t
forall (d :: * -> *) t. Distribution d t => d t -> RVar t
rvar d t
d) s
src

-- This instance overlaps with the other, but because RVarT is not a Distribution there is no conflict.
instance Lift m n => Sampleable (RVarT m) n t where
    sampleFrom :: s -> RVarT m t -> n t
sampleFrom src :: s
src x :: RVarT m t
x = RVarT m t -> s -> n t
forall (n :: * -> *) (m :: * -> *) s a.
(Lift n m, RandomSource m s) =>
RVarT n a -> s -> m a
runRVarT RVarT m t
x s
src

-- |Sample a random variable using the default source of entropy for the
-- monad in which the sampling occurs.
sample :: (Sampleable d m t, MonadRandom m) => d t -> m t
sample :: d t -> m t
sample = StdRandom -> d t -> m t
forall (d :: * -> *) (m :: * -> *) t s.
(Sampleable d m t, RandomSource m s) =>
s -> d t -> m t
sampleFrom StdRandom
StdRandom

-- |Sample a random variable in a \"functional\" style.  Typical instantiations
-- of @s@ are @System.Random.StdGen@ or @System.Random.Mersenne.Pure64.PureMT@.
sampleState :: (Sampleable d (State s) t, MonadRandom (State s)) => d t -> s -> (t, s)
sampleState :: d t -> s -> (t, s)
sampleState thing :: d t
thing = State s t -> s -> (t, s)
forall s a. State s a -> s -> (a, s)
runState (d t -> State s t
forall (d :: * -> *) (m :: * -> *) t.
(Sampleable d m t, MonadRandom m) =>
d t -> m t
sample d t
thing)

-- |Sample a random variable in a \"semi-functional\" style.  Typical instantiations
-- of @s@ are @System.Random.StdGen@ or @System.Random.Mersenne.Pure64.PureMT@.
sampleStateT :: (Sampleable d (StateT s m) t, MonadRandom (StateT s m)) => d t -> s -> m (t, s)
sampleStateT :: d t -> s -> m (t, s)
sampleStateT thing :: d t
thing = StateT s m t -> s -> m (t, s)
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateT (d t -> StateT s m t
forall (d :: * -> *) (m :: * -> *) t.
(Sampleable d m t, MonadRandom m) =>
d t -> m t
sample d t
thing)