random-1.2.0: src/System/Random/Stateful.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : System.Random.Stateful
-- Copyright : (c) The University of Glasgow 2001
-- License : BSD-style (see the file LICENSE in the 'random' repository)
-- Maintainer : libraries@haskell.org
-- Stability : stable
--
-- This library deals with the common task of pseudo-random number generation.
module System.Random.Stateful
(
-- * Pure Random Generator
module System.Random
-- * Monadic Random Generator
-- $introduction
-- * Usage
-- $usagemonadic
-- * Mutable pseudo-random number generator interfaces
-- $interfaces
, StatefulGen(..)
, FrozenGen(..)
, RandomGenM(..)
, withMutableGen
, withMutableGen_
, randomM
, randomRM
, splitGenM
-- * Monadic adapters for pure pseudo-random number generators #monadicadapters#
-- $monadicadapters
-- ** Pure adapter
, StateGen(..)
, StateGenM(..)
, runStateGen
, runStateGen_
, runStateGenT
, runStateGenT_
, runStateGenST
-- ** Mutable adapter with atomic operations
, AtomicGen(..)
, AtomicGenM(..)
, newAtomicGenM
, applyAtomicGen
-- ** Mutable adapter in 'IO'
, IOGen(..)
, IOGenM(..)
, newIOGenM
, applyIOGen
-- ** Mutable adapter in 'ST'
, STGen(..)
, STGenM(..)
, newSTGenM
, applySTGen
, runSTGen
, runSTGen_
-- * Pseudo-random values of various types
-- $uniform
, Uniform(..)
, uniformListM
, UniformRange(..)
-- * Generators for sequences of pseudo-random bytes
, genShortByteStringIO
, genShortByteStringST
, uniformByteStringM
, uniformDouble01M
, uniformDoublePositive01M
, uniformFloat01M
, uniformFloatPositive01M
-- * Appendix
-- ** How to implement 'StatefulGen'
-- $implementmonadrandom
-- ** Floating point number caveats #fpcaveats#
-- $floating
-- * References
-- $references
) where
import Control.DeepSeq
import Control.Monad.IO.Class
import Control.Monad.ST
import Control.Monad.State.Strict
import Data.IORef
import Data.STRef
import Foreign.Storable
import System.Random
import System.Random.Internal
-- $introduction
--
-- This module provides type classes and instances for the following concepts:
--
-- [Monadic pseudo-random number generators] 'StatefulGen' is an interface to
-- monadic pseudo-random number generators.
--
-- [Monadic adapters] 'StateGenM', 'AtomicGenM', 'IOGenM' and 'STGenM' turn a
-- 'RandomGen' instance into a 'StatefulGen' instance.
--
-- [Drawing from a range] 'UniformRange' is used to generate a value of a
-- type uniformly within a range.
--
-- This library provides instances of 'UniformRange' for many common
-- numeric types.
--
-- [Drawing from the entire domain of a type] 'Uniform' is used to generate a
-- value of a type uniformly over all possible values of that type.
--
-- This library provides instances of 'Uniform' for many common bounded
-- numeric types.
--
-- $usagemonadic
--
-- In monadic code, use the relevant 'Uniform' and 'UniformRange' instances to
-- generate pseudo-random values via 'uniformM' and 'uniformRM', respectively.
--
-- As an example, @rollsM@ generates @n@ pseudo-random values of @Word@ in the
-- range @[1, 6]@ in a 'StatefulGen' context; given a /monadic/ pseudo-random
-- number generator, you can run this probabilistic computation as follows:
--
-- >>> :{
-- let rollsM :: StatefulGen g m => Int -> g -> m [Word]
-- rollsM n = replicateM n . uniformRM (1, 6)
-- in do
-- monadicGen <- MWC.create
-- rollsM 10 monadicGen :: IO [Word]
-- :}
-- [3,4,3,1,4,6,1,6,1,4]
--
-- Given a /pure/ pseudo-random number generator, you can run the monadic
-- pseudo-random number computation @rollsM@ in an 'IO' or 'ST' context by
-- applying a monadic adapter like 'AtomicGenM', 'IOGenM' or 'STGenM'
-- (see [monadic-adapters](#monadicadapters)) to the pure pseudo-random number
-- generator.
--
-- >>> :{
-- let rollsM :: StatefulGen g m => Int -> g -> m [Word]
-- rollsM n = replicateM n . uniformRM (1, 6)
-- pureGen = mkStdGen 42
-- in
-- newIOGenM pureGen >>= rollsM 10 :: IO [Word]
-- :}
-- [1,1,3,2,4,5,3,4,6,2]
-------------------------------------------------------------------------------
-- Pseudo-random number generator interfaces
-------------------------------------------------------------------------------
-- $interfaces
--
-- Pseudo-random number generators come in two flavours: /pure/ and /monadic/.
--
-- ['System.Random.RandomGen': pure pseudo-random number generators]
-- See "System.Random" module.
--
-- ['StatefulGen': monadic pseudo-random number generators] These generators
-- mutate their own state as they produce pseudo-random values. They
-- generally live in 'ST' or 'IO' or some transformer that implements
-- @PrimMonad@.
--
-------------------------------------------------------------------------------
-- Monadic adapters
-------------------------------------------------------------------------------
-- $monadicadapters
--
-- Pure pseudo-random number generators can be used in monadic code via the
-- adapters 'StateGenM', 'AtomicGenM', 'IOGenM' and 'STGenM'.
--
-- * 'StateGenM' can be used in any state monad. With strict 'StateT' there is
-- no performance overhead compared to using the 'RandomGen' instance
-- directly. 'StateGenM' is /not/ safe to use in the presence of exceptions
-- and concurrency.
--
-- * 'AtomicGenM' is safe in the presence of exceptions and concurrency since
-- it performs all actions atomically.
--
-- * 'IOGenM' is a wrapper around an 'IORef' that holds a pure generator.
-- 'IOGenM' is safe in the presence of exceptions, but not concurrency.
--
-- * 'STGenM' is a wrapper around an 'STRef' that holds a pure generator.
-- 'STGenM' is safe in the presence of exceptions, but not concurrency.
-- | Interface to operations on 'RandomGen' wrappers like 'IOGenM' and 'StateGenM'.
--
-- @since 1.2.0
class (RandomGen r, StatefulGen g m) => RandomGenM g r m | g -> r where
applyRandomGenM :: (r -> (a, r)) -> g -> m a
-- | Splits a pseudo-random number generator into two. Overwrites the mutable
-- wrapper with one of the resulting generators and returns the other.
--
-- @since 1.2.0
splitGenM :: RandomGenM g r m => g -> m r
splitGenM = applyRandomGenM split
instance (RandomGen r, MonadIO m) => RandomGenM (IOGenM r) r m where
applyRandomGenM = applyIOGen
instance (RandomGen r, MonadIO m) => RandomGenM (AtomicGenM r) r m where
applyRandomGenM = applyAtomicGen
instance (RandomGen r, MonadState r m) => RandomGenM (StateGenM r) r m where
applyRandomGenM f _ = state f
instance RandomGen r => RandomGenM (STGenM r s) r (ST s) where
applyRandomGenM = applySTGen
-- | Runs a mutable pseudo-random number generator from its 'Frozen' state.
--
-- ====__Examples__
--
-- >>> import Data.Int (Int8)
-- >>> withMutableGen (IOGen (mkStdGen 217)) (uniformListM 5) :: IO ([Int8], IOGen StdGen)
-- ([-74,37,-50,-2,3],IOGen {unIOGen = StdGen {unStdGen = SMGen 4273268533320920145 15251669095119325999}})
--
-- @since 1.2.0
withMutableGen :: FrozenGen f m => f -> (MutableGen f m -> m a) -> m (a, f)
withMutableGen fg action = do
g <- thawGen fg
res <- action g
fg' <- freezeGen g
pure (res, fg')
-- | Same as 'withMutableGen', but only returns the generated value.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> withMutableGen_ (IOGen pureGen) (uniformRM (1 :: Int, 6 :: Int))
-- 4
--
-- @since 1.2.0
withMutableGen_ :: FrozenGen f m => f -> (MutableGen f m -> m a) -> m a
withMutableGen_ fg action = fst <$> withMutableGen fg action
-- | Generates a list of pseudo-random values.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> g <- newIOGenM pureGen
-- >>> uniformListM 10 g :: IO [Bool]
-- [True,True,True,True,False,True,True,False,False,False]
--
-- @since 1.2.0
uniformListM :: (StatefulGen g m, Uniform a) => Int -> g -> m [a]
uniformListM n gen = replicateM n (uniformM gen)
-- | Generates a pseudo-random value using monadic interface and `Random` instance.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> g <- newIOGenM pureGen
-- >>> randomM g :: IO Double
-- 0.5728354935654512
--
-- @since 1.2.0
randomM :: (RandomGenM g r m, Random a) => g -> m a
randomM = applyRandomGenM random
-- | Generates a pseudo-random value using monadic interface and `Random` instance.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> g <- newIOGenM pureGen
-- >>> randomRM (1, 100) g :: IO Int
-- 52
--
-- @since 1.2.0
randomRM :: (RandomGenM g r m, Random a) => (a, a) -> g -> m a
randomRM r = applyRandomGenM (randomR r)
-- | Wraps an 'IORef' that holds a pure pseudo-random number generator. All
-- operations are performed atomically.
--
-- * 'AtomicGenM' is safe in the presence of exceptions and concurrency.
-- * 'AtomicGenM' is the slowest of the monadic adapters due to the overhead
-- of its atomic operations.
--
-- @since 1.2.0
newtype AtomicGenM g = AtomicGenM { unAtomicGenM :: IORef g}
-- | Frozen version of mutable `AtomicGenM` generator
--
-- @since 1.2.0
newtype AtomicGen g = AtomicGen { unAtomicGen :: g}
deriving (Eq, Ord, Show, RandomGen, Storable, NFData)
-- | Creates a new 'AtomicGenM'.
--
-- @since 1.2.0
newAtomicGenM :: MonadIO m => g -> m (AtomicGenM g)
newAtomicGenM = fmap AtomicGenM . liftIO . newIORef
instance (RandomGen g, MonadIO m) => StatefulGen (AtomicGenM g) m where
uniformWord32R r = applyAtomicGen (genWord32R r)
{-# INLINE uniformWord32R #-}
uniformWord64R r = applyAtomicGen (genWord64R r)
{-# INLINE uniformWord64R #-}
uniformWord8 = applyAtomicGen genWord8
{-# INLINE uniformWord8 #-}
uniformWord16 = applyAtomicGen genWord16
{-# INLINE uniformWord16 #-}
uniformWord32 = applyAtomicGen genWord32
{-# INLINE uniformWord32 #-}
uniformWord64 = applyAtomicGen genWord64
{-# INLINE uniformWord64 #-}
uniformShortByteString n = applyAtomicGen (genShortByteString n)
instance (RandomGen g, MonadIO m) => FrozenGen (AtomicGen g) m where
type MutableGen (AtomicGen g) m = AtomicGenM g
freezeGen = fmap AtomicGen . liftIO . readIORef . unAtomicGenM
thawGen (AtomicGen g) = newAtomicGenM g
-- | Atomically applies a pure operation to the wrapped pseudo-random number
-- generator.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> g <- newAtomicGenM pureGen
-- >>> applyAtomicGen random g :: IO Int
-- 7879794327570578227
--
-- @since 1.2.0
applyAtomicGen :: MonadIO m => (g -> (a, g)) -> (AtomicGenM g) -> m a
applyAtomicGen op (AtomicGenM gVar) =
liftIO $ atomicModifyIORef' gVar $ \g ->
case op g of
(a, g') -> (g', a)
{-# INLINE applyAtomicGen #-}
-- | Wraps an 'IORef' that holds a pure pseudo-random number generator.
--
-- * 'IOGenM' is safe in the presence of exceptions, but not concurrency.
-- * 'IOGenM' is slower than 'StateGenM' due to the extra pointer indirection.
-- * 'IOGenM' is faster than 'AtomicGenM' since the 'IORef' operations used by
-- 'IOGenM' are not atomic.
--
-- An example use case is writing pseudo-random bytes into a file:
--
-- >>> import UnliftIO.Temporary (withSystemTempFile)
-- >>> import Data.ByteString (hPutStr)
-- >>> let ioGen g = withSystemTempFile "foo.bin" $ \_ h -> uniformRM (0, 100) g >>= flip uniformByteStringM g >>= hPutStr h
--
-- and then run it:
--
-- >>> newIOGenM (mkStdGen 1729) >>= ioGen
--
-- @since 1.2.0
newtype IOGenM g = IOGenM { unIOGenM :: IORef g }
-- | Frozen version of mutable `IOGenM` generator
--
-- @since 1.2.0
newtype IOGen g = IOGen { unIOGen :: g }
deriving (Eq, Ord, Show, RandomGen, Storable, NFData)
-- | Creates a new 'IOGenM'.
--
-- @since 1.2.0
newIOGenM :: MonadIO m => g -> m (IOGenM g)
newIOGenM = fmap IOGenM . liftIO . newIORef
instance (RandomGen g, MonadIO m) => StatefulGen (IOGenM g) m where
uniformWord32R r = applyIOGen (genWord32R r)
{-# INLINE uniformWord32R #-}
uniformWord64R r = applyIOGen (genWord64R r)
{-# INLINE uniformWord64R #-}
uniformWord8 = applyIOGen genWord8
{-# INLINE uniformWord8 #-}
uniformWord16 = applyIOGen genWord16
{-# INLINE uniformWord16 #-}
uniformWord32 = applyIOGen genWord32
{-# INLINE uniformWord32 #-}
uniformWord64 = applyIOGen genWord64
{-# INLINE uniformWord64 #-}
uniformShortByteString n = applyIOGen (genShortByteString n)
instance (RandomGen g, MonadIO m) => FrozenGen (IOGen g) m where
type MutableGen (IOGen g) m = IOGenM g
freezeGen = fmap IOGen . liftIO . readIORef . unIOGenM
thawGen (IOGen g) = newIOGenM g
-- | Applies a pure operation to the wrapped pseudo-random number generator.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> g <- newIOGenM pureGen
-- >>> applyIOGen random g :: IO Int
-- 7879794327570578227
--
-- @since 1.2.0
applyIOGen :: MonadIO m => (g -> (a, g)) -> IOGenM g -> m a
applyIOGen f (IOGenM ref) = liftIO $ do
g <- readIORef ref
case f g of
(!a, !g') -> a <$ writeIORef ref g'
{-# INLINE applyIOGen #-}
-- | Wraps an 'STRef' that holds a pure pseudo-random number generator.
--
-- * 'STGenM' is safe in the presence of exceptions, but not concurrency.
-- * 'STGenM' is slower than 'StateGenM' due to the extra pointer indirection.
--
-- @since 1.2.0
newtype STGenM g s = STGenM { unSTGenM :: STRef s g }
-- | Frozen version of mutable `STGenM` generator
--
-- @since 1.2.0
newtype STGen g = STGen { unSTGen :: g }
deriving (Eq, Ord, Show, RandomGen, Storable, NFData)
-- | Creates a new 'STGenM'.
--
-- @since 1.2.0
newSTGenM :: g -> ST s (STGenM g s)
newSTGenM = fmap STGenM . newSTRef
instance RandomGen g => StatefulGen (STGenM g s) (ST s) where
uniformWord32R r = applySTGen (genWord32R r)
{-# INLINE uniformWord32R #-}
uniformWord64R r = applySTGen (genWord64R r)
{-# INLINE uniformWord64R #-}
uniformWord8 = applySTGen genWord8
{-# INLINE uniformWord8 #-}
uniformWord16 = applySTGen genWord16
{-# INLINE uniformWord16 #-}
uniformWord32 = applySTGen genWord32
{-# INLINE uniformWord32 #-}
uniformWord64 = applySTGen genWord64
{-# INLINE uniformWord64 #-}
uniformShortByteString n = applySTGen (genShortByteString n)
instance RandomGen g => FrozenGen (STGen g) (ST s) where
type MutableGen (STGen g) (ST s) = STGenM g s
freezeGen = fmap STGen . readSTRef . unSTGenM
thawGen (STGen g) = newSTGenM g
-- | Applies a pure operation to the wrapped pseudo-random number generator.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> (runSTGen pureGen (\g -> applySTGen random g)) :: (Int, StdGen)
-- (7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
--
-- @since 1.2.0
applySTGen :: (g -> (a, g)) -> STGenM g s -> ST s a
applySTGen f (STGenM ref) = do
g <- readSTRef ref
case f g of
(!a, !g') -> a <$ writeSTRef ref g'
{-# INLINE applySTGen #-}
-- | Runs a monadic generating action in the `ST` monad using a pure
-- pseudo-random number generator.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> (runSTGen pureGen (\g -> applySTGen random g)) :: (Int, StdGen)
-- (7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
--
-- @since 1.2.0
runSTGen :: RandomGen g => g -> (forall s . STGenM g s -> ST s a) -> (a, g)
runSTGen g action = unSTGen <$> runST (withMutableGen (STGen g) action)
-- | Runs a monadic generating action in the `ST` monad using a pure
-- pseudo-random number generator. Returns only the resulting pseudo-random
-- value.
--
-- ====__Examples__
--
-- >>> import System.Random.Stateful
-- >>> let pureGen = mkStdGen 137
-- >>> (runSTGen_ pureGen (\g -> applySTGen random g)) :: Int
-- 7879794327570578227
--
-- @since 1.2.0
runSTGen_ :: RandomGen g => g -> (forall s . STGenM g s -> ST s a) -> a
runSTGen_ g action = fst $ runSTGen g action
-- $uniform
--
-- This library provides two type classes to generate pseudo-random values:
--
-- * 'UniformRange' is used to generate a value of a type uniformly within a
-- range.
-- * 'Uniform' is used to generate a value of a type uniformly over all
-- possible values of that type.
--
-- Types may have instances for both or just one of 'UniformRange' and
-- 'Uniform'. A few examples illustrate this:
--
-- * 'Int', 'Word16' and 'Bool' are instances of both 'UniformRange' and
-- 'Uniform'.
-- * 'Integer', 'Float' and 'Double' each have an instance for 'UniformRange'
-- but no 'Uniform' instance.
-- * A hypothetical type @Radian@ representing angles by taking values in the
-- range @[0, 2π)@ has a trivial 'Uniform' instance, but no 'UniformRange'
-- instance: the problem is that two given @Radian@ values always span /two/
-- ranges, one clockwise and one anti-clockwise.
-- * It is trivial to construct a @Uniform (a, b)@ instance given
-- @Uniform a@ and @Uniform b@ (and this library provides this tuple
-- instance).
-- * On the other hand, there is no correct way to construct a
-- @UniformRange (a, b)@ instance based on just @UniformRange a@ and
-- @UniformRange b@.
-------------------------------------------------------------------------------
-- Notes
-------------------------------------------------------------------------------
-- $floating
--
-- The 'UniformRange' instances for 'Float' and 'Double' use the following
-- procedure to generate a random value in a range for @uniformRM (a, b) g@:
--
-- If \(a = b\), return \(a\). Otherwise:
--
-- 1. Generate \(x\) uniformly such that \(0 \leq x \leq 1\).
--
-- The method by which \(x\) is sampled does not cover all representable
-- floating point numbers in the unit interval. The method never generates
-- denormal floating point numbers, for example.
--
-- 2. Return \(x \cdot a + (1 - x) \cdot b\).
--
-- Due to rounding errors, floating point operations are neither
-- associative nor distributive the way the corresponding operations on
-- real numbers are. Additionally, floating point numbers admit special
-- values @NaN@ as well as negative and positive infinity.
--
-- For pathological values, step 2 can yield surprising results.
--
-- * The result may be greater than @max a b@.
--
-- >>> :{
-- let (a, b, x) = (-2.13238e-29, -2.1323799e-29, 0.27736077)
-- result = x * a + (1 - x) * b :: Float
-- in (result, result > max a b)
-- :}
-- (-2.1323797e-29,True)
--
-- * The result may be smaller than @min a b@.
--
-- >>> :{
-- let (a, b, x) = (-1.9087862, -1.908786, 0.4228573)
-- result = x * a + (1 - x) * b :: Float
-- in (result, result < min a b)
-- :}
-- (-1.9087863,True)
--
-- What happens when @NaN@ or @Infinity@ are given to 'uniformRM'? We first
-- define them as constants:
--
-- >>> nan = read "NaN" :: Float
-- >>> inf = read "Infinity" :: Float
--
-- * If at least one of \(a\) or \(b\) is @NaN@, the result is @NaN@.
--
-- >>> let (a, b, x) = (nan, 1, 0.5) in x * a + (1 - x) * b
-- NaN
-- >>> let (a, b, x) = (-1, nan, 0.5) in x * a + (1 - x) * b
-- NaN
--
-- * If \(a\) is @-Infinity@ and \(b\) is @Infinity@, the result is @NaN@.
-- >>> let (a, b, x) = (-inf, inf, 0.5) in x * a + (1 - x) * b
-- NaN
--
-- * Otherwise, if \(a\) is @Infinity@ or @-Infinity@, the result is \(a\).
--
-- >>> let (a, b, x) = (inf, 1, 0.5) in x * a + (1 - x) * b
-- Infinity
-- >>> let (a, b, x) = (-inf, 1, 0.5) in x * a + (1 - x) * b
-- -Infinity
--
-- * Otherwise, if \(b\) is @Infinity@ or @-Infinity@, the result is \(b\).
--
-- >>> let (a, b, x) = (1, inf, 0.5) in x * a + (1 - x) * b
-- Infinity
-- >>> let (a, b, x) = (1, -inf, 0.5) in x * a + (1 - x) * b
-- -Infinity
--
-- Note that the [GCC 10.1.0 C++ standard library](https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/random.h;h=19307fbc3ca401976ef6823e8fda893e4a263751;hb=63fa67847628e5f358e7e2e7edb8314f0ee31f30#l1859),
-- the [Java 10 standard library](https://docs.oracle.com/javase/10/docs/api/java/util/Random.html#doubles%28double,double%29)
-- and [CPython 3.8](https://github.com/python/cpython/blob/3.8/Lib/random.py#L417)
-- use the same procedure to generate floating point values in a range.
--
-- $implementmonadrandom
--
-- Typically, a monadic pseudo-random number generator has facilities to save
-- and restore its internal state in addition to generating pseudo-random numbers.
--
-- Here is an example instance for the monadic pseudo-random number generator
-- from the @mwc-random@ package:
--
-- > instance (s ~ PrimState m, PrimMonad m) => StatefulGen (MWC.Gen s) m where
-- > uniformWord8 = MWC.uniform
-- > uniformWord16 = MWC.uniform
-- > uniformWord32 = MWC.uniform
-- > uniformWord64 = MWC.uniform
-- > uniformShortByteString n g = unsafeSTToPrim (genShortByteStringST n (MWC.uniform g))
--
-- > instance PrimMonad m => FrozenGen MWC.Seed m where
-- > type MutableGen MWC.Seed m = MWC.Gen (PrimState m)
-- > thawGen = MWC.restore
-- > freezeGen = MWC.save
--
-- === @FrozenGen@
--
-- `FrozenGen` gives us ability to use any stateful pseudo-random number generator in its
-- immutable form, if one exists that is. This concept is commonly known as a seed, which
-- allows us to save and restore the actual mutable state of a pseudo-random number
-- generator. The biggest benefit that can be drawn from a polymorphic access to a
-- stateful pseudo-random number generator in a frozen form is the ability to serialize,
-- deserialize and possibly even use the stateful generator in a pure setting without
-- knowing the actual type of a generator ahead of time. For example we can write a
-- function that accepts a frozen state of some pseudo-random number generator and
-- produces a short list with random even integers.
--
-- >>> import Data.Int (Int8)
-- >>> :{
-- myCustomRandomList :: FrozenGen f m => f -> m [Int8]
-- myCustomRandomList f =
-- withMutableGen_ f $ \gen -> do
-- len <- uniformRM (5, 10) gen
-- replicateM len $ do
-- x <- uniformM gen
-- pure $ if even x then x else x + 1
-- :}
--
-- and later we can apply it to a frozen version of a stateful generator, such as `STGen`:
--
-- >>> print $ runST $ myCustomRandomList (STGen (mkStdGen 217))
-- [-50,-2,4,-8,-58,-40,24,-32,-110,24]
--
-- or a @Seed@ from @mwc-random@:
--
-- >>> import Data.Vector.Primitive as P
-- >>> print $ runST $ myCustomRandomList (MWC.toSeed (P.fromList [1,2,3]))
-- [24,40,10,40,-8,48,-78,70,-12]
--
-- Alternatively, instead of discarding the final state of the generator, as it happens
-- above, we could have used `withMutableGen`, which together with the result would give
-- us back its frozen form. This would allow us to store the end state of our generator
-- somewhere for the later reuse.
--
--
-- $references
--
-- 1. Guy L. Steele, Jr., Doug Lea, and Christine H. Flood. 2014. Fast
-- splittable pseudorandom number generators. In Proceedings of the 2014 ACM
-- International Conference on Object Oriented Programming Systems Languages &
-- Applications (OOPSLA '14). ACM, New York, NY, USA, 453-472. DOI:
-- <https://doi.org/10.1145/2660193.2660195>
-- $setup
-- >>> import Control.Monad.Primitive
-- >>> import qualified System.Random.MWC as MWC
--
-- >>> :set -XFlexibleContexts
-- >>> :set -XFlexibleInstances
-- >>> :set -XMultiParamTypeClasses
-- >>> :set -XTypeFamilies
-- >>> :set -XUndecidableInstances
--
-- >>> :{
-- instance (s ~ PrimState m, PrimMonad m) => StatefulGen (MWC.Gen s) m where
-- uniformWord8 = MWC.uniform
-- uniformWord16 = MWC.uniform
-- uniformWord32 = MWC.uniform
-- uniformWord64 = MWC.uniform
-- uniformShortByteString n g = unsafeSTToPrim (genShortByteStringST n (MWC.uniform g))
-- instance PrimMonad m => FrozenGen MWC.Seed m where
-- type MutableGen MWC.Seed m = MWC.Gen (PrimState m)
-- thawGen = MWC.restore
-- freezeGen = MWC.save
-- :}
--