HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

  • Packagerandom-1.2.1.3
  • Exports49
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceStateful.hs

Pure Random Generator

0 declarations

Monadic Random Generator

0 declarations

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

,

STGenM

and

TGenM

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.

Usage

0 declarations

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:

Example1 expression
:{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) to the pure pseudo-random number generator.

Example1 expression
:{let rollsM :: StatefulGen g m => Int -> g -> m [Word]    rollsM n = replicateM n . uniformRM (1, 6)    pureGen = mkStdGen 42in    newIOGenM pureGen >>= rollsM 10 :: IO [Word]:}[1,1,3,2,4,5,3,4,6,2]

Mutable pseudo-random number generator interfaces

8 declarations

Pseudo-random number generators come in two flavours: pure and monadic.

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

.

classclass Monad m => StatefulGen g (m :: Type -> Type) where
#

StatefulGen is an interface to monadic pseudo-random number generators.

Methods

Instances5StatefulGen
classclass StatefulGen (MutableGen f m) m => FrozenGen f (m :: Type -> Type) where
#

This class is designed for stateful pseudo-random number generators that can be saved as and restored from an immutable data type.

Associated types

Methods

  • freezeGen :: MutableGen f m -> m f

    Saves the state of the pseudo-random number generator as a frozen seed.

  • thawGen :: f -> m (MutableGen f m)

    Restores the pseudo-random number generator from its frozen seed.

Instances5FrozenGen
classclass (RandomGen r, StatefulGen g m) => RandomGenM g r (m :: Type -> Type) | g -> r where
#

Interface to operations on RandomGen wrappers like IOGenM and StateGenM.

Methods

Instances5RandomGenM
valuewithMutableGen :: FrozenGen f m => f -> (MutableGen f m -> m a) -> m (a, f)
#

Runs a mutable pseudo-random number generator from its FrozenGen state.

Examples
Example2 expressions
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}})
valuewithMutableGen_ :: FrozenGen f m => f -> (MutableGen f m -> m a) -> m a
#

Same as withMutableGen, but only returns the generated value.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137withMutableGen_ (IOGen pureGen) (uniformRM (1 :: Int, 6 :: Int))4
valuerandomM :: (RandomGenM g r m, Random a) => g -> m a
#

Generates a pseudo-random value using monadic interface and Random instance.

Examples
Example4 expressions
import System.Random.Statefullet pureGen = mkStdGen 137g <- newIOGenM pureGenrandomM g :: IO Double0.5728354935654512
valuerandomRM :: (RandomGenM g r m, Random a) => (a, a) -> g -> m a
#

Generates a pseudo-random value using monadic interface and Random instance.

Examples
Example4 expressions
import System.Random.Statefullet pureGen = mkStdGen 137g <- newIOGenM pureGenrandomRM (1, 100) g :: IO Int52
valuesplitGenM :: RandomGenM g r m => g -> m r
#

Splits a pseudo-random number generator into two. Overwrites the mutable wrapper with one of the resulting generators and returns the other.

Monadic adapters for pure pseudo-random number generators

0 declarations

Pure pseudo-random number generators can be used in monadic code via the adapters StateGenM, AtomicGenM, IOGenM, STGenM and TGenM

  • 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.

  • TGenM is a wrapper around a TVar that holds a pure generator. TGenM can be used in a software transactional memory monad STM. It is not as performant as AtomicGenM, but it can provide stronger guarantees in a concurrent setting.

Pure adapter

newtypenewtype StateGen g
#

Wrapper for pure state gen, which acts as an immutable seed for the corresponding stateful generator StateGenM

Constructors

Instances8Eq, Ord, Show, Storable, NFData, RandomGen, …
valuerunStateGen :: RandomGen g => g -> (StateGenM g -> State g a) -> (a, g)
#

Runs a monadic generating action in the State monad using a pure pseudo-random number generator.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137runStateGen pureGen randomM :: (Int, StdGen)(7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
valuerunStateGen_ :: RandomGen g => g -> (StateGenM g -> State g a) -> a
#

Runs a monadic generating action in the State monad using a pure pseudo-random number generator. Returns only the resulting pseudo-random value.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137runStateGen_ pureGen randomM :: Int7879794327570578227
valuerunStateGenT
  1. :: RandomGen g
  2. => g
  3. -> StateGenM g -> StateT g m a
  4. -> m (a, g)
#

Runs a monadic generating action in the StateT monad using a pure pseudo-random number generator.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137runStateGenT pureGen randomM :: IO (Int, StdGen)(7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
valuerunStateGenT_
  1. :: (RandomGen g, Functor f)
  2. => g
  3. -> StateGenM g -> StateT g f a
  4. -> f a
#

Runs a monadic generating action in the StateT monad using a pure pseudo-random number generator. Returns only the resulting pseudo-random value.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137runStateGenT_ pureGen randomM :: IO Int7879794327570578227

Mutable adapter with atomic operations

newtypenewtype AtomicGen g
#

Frozen version of mutable AtomicGenM generator

Constructors

Instances8Eq, Ord, Show, Storable, NFData, RandomGen, …
newtypenewtype AtomicGenM g
#

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.

Constructors

Instances2StatefulGen, RandomGenM
valueapplyAtomicGen :: MonadIO m => (g -> (a, g)) -> AtomicGenM g -> m a
#

Atomically applies a pure operation to the wrapped pseudo-random number generator.

Examples
Example4 expressions
import System.Random.Statefullet pureGen = mkStdGen 137g <- newAtomicGenM pureGenapplyAtomicGen random g :: IO Int7879794327570578227

Global mutable standard pseudo-random number generator. This is the same generator that was historically used by randomIO and randomRIO functions.

Example1 expression
replicateM 10 (uniformRM ('a', 'z') globalStdGen)"tdzxhyfvgr"

Mutable adapter in IO

newtypenewtype IOGen g
#

Frozen version of mutable IOGenM generator

Constructors

Instances8Eq, Ord, Show, Storable, NFData, RandomGen, …
newtypenewtype IOGenM g
#

Wraps an IORef that holds a pure pseudo-random number generator.

An example use case is writing pseudo-random bytes into a file:

Example3 expressions
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:

Example1 expression
newIOGenM (mkStdGen 1729) >>= ioGen

Constructors

Instances2StatefulGen, RandomGenM
valueapplyIOGen :: MonadIO m => (g -> (a, g)) -> IOGenM g -> m a
#

Applies a pure operation to the wrapped pseudo-random number generator.

Examples
Example4 expressions
import System.Random.Statefullet pureGen = mkStdGen 137g <- newIOGenM pureGenapplyIOGen random g :: IO Int7879794327570578227

Mutable adapter in ST

newtypenewtype STGen g
#

Frozen version of mutable STGenM generator

Constructors

Instances8Eq, Ord, Show, Storable, NFData, RandomGen, …
valueapplySTGen :: (g -> (a, g)) -> STGenM g s -> ST s a
#

Applies a pure operation to the wrapped pseudo-random number generator.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137(runSTGen pureGen (\g -> applySTGen random g)) :: (Int, StdGen)(7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
valuerunSTGen :: RandomGen g => g -> (forall s. STGenM g s -> ST s a) -> (a, g)
#

Runs a monadic generating action in the ST monad using a pure pseudo-random number generator.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137(runSTGen pureGen (\g -> applySTGen random g)) :: (Int, StdGen)(7879794327570578227,StdGen {unStdGen = SMGen 11285859549637045894 7641485672361121627})
valuerunSTGen_ :: RandomGen g => g -> (forall s. STGenM g s -> ST s a) -> a
#

Runs a monadic generating action in the ST monad using a pure pseudo-random number generator. Returns only the resulting pseudo-random value.

Examples
Example3 expressions
import System.Random.Statefullet pureGen = mkStdGen 137(runSTGen_ pureGen (\g -> applySTGen random g)) :: Int7879794327570578227

Mutable adapter in STM

newtypenewtype TGen g
#

Frozen version of mutable TGenM generator

Constructors

Instances8Eq, Ord, Show, Storable, NFData, RandomGen, …
valueapplyTGen :: (g -> (a, g)) -> TGenM g -> STM a
#

Applies a pure operation to the wrapped pseudo-random number generator.

Examples
Example6 expressions
import Control.Concurrent.STMimport System.Random.Statefulimport Data.Int (Int32)let pureGen = mkStdGen 137stmGen <- newTGenMIO pureGenatomically $ applyTGen uniform stmGen :: IO Int32637238067

Pseudo-random values of various types

4 declarations

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.

classclass Uniform a where
#

The class of types for which a uniformly distributed value can be drawn from all possible values of the type.

Methods

  • uniformM :: StatefulGen g m => g -> m a

    Generates a value uniformly distributed over all possible values of that type.

    There is a default implementation via Generic:

    Example7 expressions
    :set -XDeriveGeneric -XDeriveAnyClassimport GHC.Generics (Generic)import System.Random.Statefuldata MyBool = MyTrue | MyFalse deriving (Show, Generic, Finite, Uniform)data Action = Code MyBool | Eat (Maybe Bool) | Sleep deriving (Show, Generic, Finite, Uniform)gen <- newIOGenM (mkStdGen 42)uniformListM 10 gen :: IO [Action][Code MyTrue,Code MyTrue,Eat Nothing,Code MyFalse,Eat (Just False),Eat (Just True),Eat Nothing,Eat (Just False),Sleep,Code MyFalse]
Instances39Uniform, …
valueuniformListM :: (StatefulGen g m, Uniform a) => Int -> g -> m [a]
#

Generates a list of pseudo-random values.

Examples
Example4 expressions
import System.Random.Statefullet pureGen = mkStdGen 137g <- newIOGenM pureGenuniformListM 10 g :: IO [Bool][True,True,True,True,False,True,True,False,False,False]
valueuniformViaFiniteM
  1. :: (StatefulGen g m, Generic a, GFinite (Rep a))
  2. => g
  3. -> m a
#

A definition of Uniform for Finite types. If your data has several fields of sub-Word cardinality, this instance may be more efficient than one, derived via Generic and GUniform.

Example7 expressions
:set -XDeriveGeneric -XDeriveAnyClassimport GHC.Generics (Generic)import System.Random.Statefuldata Triple = Triple Word8 Word8 Word8 deriving (Show, Generic, Finite)instance Uniform Triple where uniformM = uniformViaFiniteMgen <- newIOGenM (mkStdGen 42)uniformListM 5 gen :: IO [Triple][Triple 60 226 48,Triple 234 194 151,Triple 112 96 95,Triple 51 251 15,Triple 6 0 208]
classclass UniformRange a where
#

The class of types for which a uniformly distributed value can be drawn from a range.

Methods

  • uniformRM :: StatefulGen g m => (a, a) -> g -> m a

    Generates a value uniformly distributed over the provided range, which is interpreted as inclusive in the lower and upper bound.

    • uniformRM (1 :: Int, 4 :: Int) generates values uniformly from the set \{1,2,3,4\}

    • uniformRM (1 :: Float, 4 :: Float) generates values uniformly from the set \{x\;|\;1 \le x \le 4\}

    The following law should hold to make the function always defined:

    uniformRM (a, b) = uniformRM (b, a)
Instances39UniformRange, …

Generators for sequences of pseudo-random bytes

9 declarations
valueuniformEnumM :: (Enum a, Bounded a, StatefulGen g m) => g -> m a
#

Generates uniformly distributed Enum. One can use it to define a Uniform instance:

data Colors = Red | Green | Blue deriving (Enum, Bounded)
instance Uniform Colors where uniformM = uniformEnumM
valueuniformEnumRM :: (Enum a, StatefulGen g m) => (a, a) -> g -> m a
#

Generates uniformly distributed Enum in the given range. One can use it to define a UniformRange instance:

data Colors = Red | Green | Blue deriving (Enum)
instance UniformRange Colors where
  uniformRM = uniformEnumRM
  inInRange (lo, hi) x = isInRange (fromEnum lo, fromEnum hi) (fromEnum x)

Appendix

0 declarations

How to implement StatefulGen

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.

Example2 expressions
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:

Example1 expression
print $ runST $ myCustomRandomList (STGen (mkStdGen 217))[-50,-2,4,-8,-58,-40,24,-32,-110,24]

or a Seed from mwc-random:

Example2 expressions
import Data.Vector.Primitive as Pprint $ 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.

Floating point number caveats

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.

    Example1 expression
    :{let (a, b, x) = (-2.13238e-29, -2.1323799e-29, 0.27736077)    result = x * a + (1 - x) * b :: Floatin (result, result > max a b):}(-2.1323797e-29,True)
  • The result may be smaller than min a b.

    Example1 expression
    :{let (a, b, x) = (-1.9087862, -1.908786, 0.4228573)    result = x * a + (1 - x) * b :: Floatin (result, result < min a b):}(-1.9087863,True)

What happens when NaN or Infinity are given to uniformRM? We first define them as constants:

Example2 expressions
nan = read "NaN" :: Floatinf = read "Infinity" :: Float
  • If at least one of a or b is NaN, the result is NaN.

    Example2 expressions
    let (a, b, x) = (nan, 1, 0.5) in x * a + (1 - x) * bNaNlet (a, b, x) = (-1, nan, 0.5) in x * a + (1 - x) * bNaN
  • If a is -Infinity and b is Infinity, the result is NaN.

    Example1 expression
    let (a, b, x) = (-inf, inf, 0.5) in x * a + (1 - x) * bNaN
  • Otherwise, if a is Infinity or -Infinity, the result is a.

    Example2 expressions
    let (a, b, x) = (inf, 1, 0.5) in x * a + (1 - x) * bInfinitylet (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.

    Example2 expressions
    let (a, b, x) = (1, inf, 0.5) in x * a + (1 - x) * bInfinitylet (a, b, x) = (1, -inf, 0.5) in x * a + (1 - x) * b-Infinity

Note that the GCC 10.1.0 C++ standard library, the Java 10 standard library and CPython 3.8 use the same procedure to generate floating point values in a range.

References

0 declarations
  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