is an interface to pure
pseudo-random number generators.
StdGen, the standard pseudo-random number generator provided in this
library, is an instance of RandomGen. It uses the SplitMix
implementation provided by the
splitmix package.
Programmers may, of course, supply their own instances of RandomGen.
Usage
0 declarations
In pure code, use uniform and uniformR to generate pseudo-random values
with a pure pseudo-random number generator like StdGen.
Example1 expression
>>> :{let rolls :: RandomGen g => Int -> g -> [Word] rolls n = take n . unfoldr (Just . uniformR (1, 6)) pureGen = mkStdGen 137in rolls 10 pureGen :: [Word]:}[4,2,6,1,6,6,5,1,1,5]
To run use a monadic pseudo-random computation in pure code with a pure
pseudo-random number generator, use runStateGen and its variants.
Example1 expression
>>> :{let rollsM :: StatefulGen g m => Int -> g -> m [Word] rollsM n = replicateM n . uniformRM (1, 6) pureGen = mkStdGen 137in runStateGen_ pureGen (rollsM 10) :: [Word]:}[4,2,6,1,6,6,5,1,1,5]
Pure number generator interface
8 declarations
Pseudo-random number generators come in two flavours: pure and monadic.
These generators produce
a new pseudo-random value together with a new instance of the
pseudo-random number generator.
Pure pseudo-random number generators should implement split if they
are splittable, that is, if there is an efficient method to turn one
generator into two. The pseudo-random numbers produced by the two
resulting generators should not be correlated. See [1] for some
background on splittable pseudo-random generators.
StatefulGen: monadic pseudo-random number generators
Returns an Int that is uniformly distributed over the range returned by
genRange (including both end points), and a new generator. Using next
is inefficient as all operations go via Integer. See
here for
more details. It is thus deprecated.
Returns two distinct pseudo-random number generators.
Implementations should take care to ensure that the resulting generators
are not correlated. Some pseudo-random number generators are not
splittable. In that case, the split implementation should fail with a
descriptive error message.
Instances8RandomGen, …
RandomGenStdGenDefined in random-1.2.1.3 · System.Random.Internal
RandomGenSMGenDefined in random-1.2.1.3 · System.Random.Internal
RandomGenSMGenDefined in random-1.2.1.3 · System.Random.Internal
The class of types for which random values can be generated. Most
instances of Random will produce values that are uniformly distributed on the full
range, but for those types without a well-defined "full range" some sensible default
subrange will be selected.
Random exists primarily for backwards compatibility with version 1.1 of
this library. In new code, use the better specified Uniform and
UniformRange instead.
Takes a range (lo,hi) and a pseudo-random number generator
g, and returns a pseudo-random value uniformly distributed over the
closed interval [lo,hi], together with a new generator. It is unspecified
what happens if lo>hi, but usually the values will simply get swapped.
Example3 expressions
>>> let gen = mkStdGen 2021>>> fst $ randomR ('a', 'z') gen't'>>> fst $ randomR ('z', 'a') gen't'
For continuous types there is no requirement that the values lo and hi are ever
produced, but they may be, depending on the implementation and the interval.
There is no requirement to follow the Ord instance and the concept of range can be
defined on per type basis. For example product types will treat their values
independently:
Initialize StdGen using system entropy (i.e. /dev/urandom) when it is
available, while falling back on using system time as the seed.
Global standard pseudo-random number generator
There is a single, implicit, global pseudo-random number generator of type
StdGen, held in a global mutable variable that can be manipulated from
within the IO monad. It is also available as
globalStdGen, therefore it is recommended to use the
new System.Random.Stateful interface to explicitly operate on the global
pseudo-random number generator.
It is initialised with initStdGen, although it is possible to override its
value with setStdGen. All operations on the global pseudo-random number
generator are thread safe, however in presence of concurrency they are
naturally become non-deterministic. Moreover, relying on the global mutable
state makes it hard to know which of the dependent libraries are using it as
well, making it unpredictable in the local context. Precisely of this reason,
the global pseudo-random number generator is only suitable for uses in
applications, test suites, etc. and is advised against in development of
reusable libraries.
It is also important to note that either using StdGen with pure functions
from other sections of this module or by relying on
runStateGen from stateful interface does not only
give us deterministic behaviour without requiring IO, but it is also more
efficient.
Uses the supplied function to get a value from the current global
random generator, and updates the global generator with the new generator
returned by the function. For example, rollDice produces a pseudo-random integer
between 1 and 6:
This is an outdated function and it is recommended to switch to its
equivalent applyAtomicGen instead, possibly with the
globalStdGen if relying on the global state is
acceptable.
This function is equivalent to getStdRandomrandom and is included in
this interface for historical reasons and backwards compatibility. It is
recommended to use uniformM instead, possibly with
the globalStdGen if relying on the global state is
acceptable.
A variant of randomRM that uses the global
pseudo-random number generator globalStdGen
Example1 expression
>>> randomRIO (2020, 2100) :: IO Int2040
Similar to randomIO, this function is equivalent to getStdRandomrandomR and is included in this interface for historical reasons and
backwards compatibility. It is recommended to use
uniformRM instead, possibly with the
globalStdGen if relying on the global state is
acceptable.
Version 1.2 mostly maintains backwards compatibility with version 1.1. This
has a few consequences users should be aware of:
The type class Random is only provided for backwards compatibility.
New code should use Uniform and UniformRange instead.
The methods next and genRange in RandomGen are deprecated and only
provided for backwards compatibility. New instances of RandomGen should
implement word-based methods instead. See below for more information
about how to write a RandomGen instance.
This library provides instances for Random for some unbounded types
for backwards compatibility. For an unbounded type, there is no way
to generate a value with uniform probability out of its entire domain, so
the random implementation for unbounded types actually generates a
value based on some fixed range.
For Integer, random generates a value in the Int range. For Float
and Double, random generates a floating point value in the range [0,
1).
This library does not provide Uniform instances for any unbounded
types.
Reproducibility
If you have two builds of a particular piece of code against this library,
any deterministic function call should give the same result in the two
builds if the builds are
compiled against the same major version of this library
on the same architecture (32-bit or 64-bit)
Notes for pseudo-random number generator implementors
Consider these points when writing a RandomGen instance for a given pure
pseudo-random number generator:
If the pseudo-random number generator has a power-of-2 modulus, that is,
it natively outputs 2^n bits of randomness for some n, implement
genWord8, genWord16, genWord32 and genWord64. See below for more
details.
If the pseudo-random number generator does not have a power-of-2
modulus, implement next and genRange. See below for more details.
If the pseudo-random number generator is splittable, implement split.
If there is no suitable implementation, split should fail with a
helpful error message.
How to implement RandomGen for a pseudo-random number generator with power-of-2 modulus
You can make it an instance of RandomGen as follows:
Example1 expression
>>> :{instance RandomGen PCGen where genWord32 = stepGen split _ = error "PCG is not splittable":}
How to implement RandomGen for a pseudo-random number generator without a power-of-2 modulus
We do not recommend you implement any new pseudo-random number generators without a power-of-2 modulus.
Pseudo-random number generators without a power-of-2 modulus perform
significantly worse than pseudo-random number generators with a power-of-2
modulus with this library. This is because most functionality in this
library is based on generating and transforming uniformly pseudo-random
machine words, and generating uniformly pseudo-random machine words using a
pseudo-random number generator without a power-of-2 modulus is expensive.
The pseudo-random number generator from
L’Ecuyer (1988) natively
generates an integer value in the range [1, 2147483562]. This is the
generator used by this library before it was replaced by SplitMix in version
1.2.
Example2 expressions
>>> data LegacyGen = LegacyGen !Int32 !Int32>>> :{let legacyNext :: LegacyGen -> (Int, LegacyGen) legacyNext (LegacyGen s1 s2) = (fromIntegral z', LegacyGen s1'' s2'') where z' = if z < 1 then z + 2147483562 else z z = s1'' - s2'' k = s1 `quot` 53668 s1' = 40014 * (s1 - k * 53668) - k * 12211 s1'' = if s1' < 0 then s1' + 2147483563 else s1' k' = s2 `quot` 52774 s2' = 40692 * (s2 - k' * 52774) - k' * 3791 s2'' = if s2' < 0 then s2' + 2147483399 else s2':}
You can make it an instance of RandomGen as follows:
Example1 expression
>>> :{instance RandomGen LegacyGen where next = legacyNext genRange _ = (1, 2147483562) split _ = error "Not implemented":}
References
0 declarations
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