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

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

module Data.Random.Sample where

import Control.Monad.State
import Control.Monad.Reader
import Data.Random.Distribution
import Data.Random.Lift
import Data.Random.RVar

import System.Random.Stateful

-- |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 :: StatefulGen g m => g -> d t -> m t

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

-- 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 :: g -> RVarT m t -> n t
sampleFrom g
gen RVarT m t
x = RVarT m t -> g -> n t
forall (n :: * -> *) (m :: * -> *) g a.
(Lift n m, StatefulGen g m) =>
RVarT n a -> g -> m a
runRVarT RVarT m t
x g
gen

-- |Sample a random variable using the default source of entropy for the
-- monad in which the sampling occurs.
sample :: (Sampleable d m t, StatefulGen g m, MonadReader g m) => d t -> m t
sample :: d t -> m t
sample d t
thing = m g
forall r (m :: * -> *). MonadReader r m => m r
ask m g -> (g -> m t) -> m t
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \g
gen -> g -> d t -> m t
forall (d :: * -> *) (m :: * -> *) t g.
(Sampleable d m t, StatefulGen g m) =>
g -> d t -> m t
sampleFrom g
gen d t
thing

-- |Sample a random variable in a \"functional\" style.  Typical instantiations
-- of @s@ are @System.Random.StdGen@ or @System.Random.Mersenne.Pure64.PureMT@.
-- sample :: (Distribution d a, StatefulGen g m, MonadReader g m) => d t -> m t
-- sample thing gen = runStateGen gen (\stateGen -> sampleFrom stateGen thing)

sampleState :: (Distribution d t, RandomGen g, MonadState g m) => d t -> m t
sampleState :: d t -> m t
sampleState d t
thing = StateGenM g -> d t -> m t
forall (d :: * -> *) (m :: * -> *) t g.
(Sampleable d m t, StatefulGen g m) =>
g -> d t -> m t
sampleFrom StateGenM g
forall g. StateGenM g
StateGenM d t
thing

-- |Sample a random variable in a \"functional\" style.  Typical instantiations
-- of @g@ are @System.Random.StdGen@ or @System.Random.Mersenne.Pure64.PureMT@.
samplePure :: (Distribution d t, RandomGen g) => d t -> g -> (t, g)
samplePure :: d t -> g -> (t, g)
samplePure d t
thing g
gen = g -> (StateGenM g -> State g t) -> (t, g)
forall g a.
RandomGen g =>
g -> (StateGenM g -> State g a) -> (a, g)
runStateGen g
gen (\StateGenM g
stateGen -> StateGenM g -> d t -> State g t
forall (d :: * -> *) (m :: * -> *) t g.
(Sampleable d m t, StatefulGen g m) =>
g -> d t -> m t
sampleFrom StateGenM g
stateGen d t
thing)