{-# 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 -- :} --