Generate psuedo-random numbers and values, by constructing
generators for them. There are a bunch of basic generators like
bool and int that you can build up into fancier generators
with functions like list and map.
You run a Generator by calling the generate function, which
also takes a random Seed, and passes back a new seed. You should
never use the same seed twice because you will get the same result! If you need
random values across many frames, you should store the most recent seed in your
model. If you need several independent models, you can split seeds
into more seeds.
Generate a random value as specified by a given Generator, using a Seed
and returning a new one.
In the following example, we are trying to generate numbers between 0 and 100
with the int 0 100 generator. Each time we call generate we need to provide
a seed. This will produce a random number and a new seed to use if we want to
run other generators later.
seed0 = initialSeed2 227852860 1498709020
(x, seed1) = generate (int 0 100) seed0
(y, seed2) = generate (int 0 100) seed1
(z, seed3) = generate (int 0 100) seed2
[x, y, z] -- [85, 0, 38]Notice that we use different seeds on each line. This is important! If you use the same seed, you get the same results.
(x, seed1) = generate (int 0 100) seed0
(y, seed2) = generate (int 0 100) seed0
(z, seed3) = generate (int 0 100) seed0
[x,y,z] -- [85, 85, 85]The rest of the library is how to make generators for any kind of value you like, and how to create and manage random seeds.
Create a generator that produces boolean values. The following example simulates a coin flip that may land heads or tails.
type Flip = Heads | Tails
coinFlip : Generator Flip
coinFlip =
map (\b -> if b then Heads else Tails) boolGenerate 32-bit integers in a given range.
int 0 10 -- an integer between zero and ten
int -5 5 -- an integer between -5 and 5
int minInt maxInt -- an integer in the widest range feasibleThis function can produce values outside of the range [minInt,
maxInt] but sufficient randomness is not guaranteed.
Generate floats in a given range. The following example is a generator that produces decimals between 0 and 1.
probability : Generator Float
probability =
float 0 1Create a pair of random values. A common use of this might be to generate a point in a certain 2D space. Imagine we have a collage that is 400 pixels wide and 200 pixels tall.
randomPoint : Generator (Int,Int)
randomPoint =
pair (int -200 200) (int -100 100)Create a list of random values of a given length.
floatList : Generator (List Float)
floatList =
list 10 (float 0 1)
intList : Generator (List Int)
intList =
list 5 (int 0 100)
intPairs : Generator (List (Int, Int))
intPairs =
list 10 <| pair (int 0 100) (int 0 100)Create a generator that always produces the value provided. This is useful when creating complicated chained generators and you need to handle a simple case.
Transform the values produced by a generator. The following examples show how to generate booleans and letters based on a basic integer generator.
bool : Generator Bool
bool =
map ((==) 1) (int 0 1)
lowercaseLetter : Generator Char
lowercaseLetter =
map (\n -> Char.fromCode (n + 97)) (int 0 25)
uppercaseLetter : Generator Char
uppercaseLetter =
map (\n -> Char.fromCode (n + 65)) (int 0 25)Combine two generators.
This function is used to define things like pair where you want to
put two generators together.
pair : Generator a -> Generator b -> Generator (a,b)
pair genA genB =
map2 (,) genA genBCombine three generators. This could be used to produce random colors.
import Color
rgb : Generator Color.Color
rgb =
map3 Color.rgb (int 0 255) (int 0 255) (int 0 255)
hsl : Generator Color.Color
hsl =
map3 Color.hsl (map degrees (int 0 360)) (float 0 1) (float 0 1)Combine four generators.
import Color
rgba : Generator Color.Color
rgba =
map4 Color.rgb (int 0 255) (int 0 255) (int 0 255) (float 0 1)Combine five generators.
Map over any number of generators.
randomPerson =
person `map` genFirstName
`andMap` genLastName
`andMap` genBirthday
`andMap` genPhoneNumberChain random operations by providing a callback that accepts a randomly-generated value. This all happens inside the generator, and the seed is managed for you.
In the following example, we will generate a random letter by putting together uppercase and lowercase letters.
letter : Generator Char
letter =
bool `andThen` \b ->
if b then genUppercaseLetter else genLowercaseLetterA Seed is the source of randomness in the whole system. Whenever you want
to use a generator, you need to supply a seed. You will get back a new seed,
which you must use to generate new random numbers. If you use the same seed many
times, it will result in the same thing every time!
Seeds are created by providing initial values, which should happen only once per
program. Although this library is more forgiving of poor seed choice than core,
the best random seeds are drawn uniformly at random from the space of possible
seeds. You can get a good starting value by running
Math.floor(Math.random()*0xFFFFFFFF) in a JavaScript console.
Take two integers to fully initialize the 64-bit state of the random number generator. Only the least significant 32 bits of each integer matter.
One can generate values at random and copy them to create a reproducible random generator.
$ node
> Math.floor(Math.random()*0xFFFFFFFF)
227852860
> Math.floor(Math.random()*0xFFFFFFFF)
1498709020
-- Elm
seed0 : Seed
seed0 = initialSeed2 227852860 1498709020Alternatively, we can generate the randomized values dynamically and pass them through a port. Out program will be different every time.
-- Elm
port randomSeed : (Int, Int)
seed0 : Seed
seed0 = (uncurry initialSeed2) randomSeed
-- JS
Elm.fullscreen(Elm.ModuleName,
{randomSeed: [Math.floor(Math.random()*0xFFFFFFFF), Math.floor(Math.random()*0xFFFFFFFF)] })Like initialSeed2, but takes only one integer. Mostly for compatibility
with core.
Split a seed into two new seeds. Each seed will generate different random numbers. Splitting is a reproducible operation; just like generating numbers, it will be the same every time.
Let's say you have have many independent components which will each want to generate many random numbers. After splitting a seed, you can pass one of the new seeds to a component, and keep the other to repeat the process.
makeComponents : Seed -> List (Seed -> Component) -> (List Component, Seed)
makeComponents seed constructors =
case constructors of
[] ->
([], seed)
c::cs ->
let
(seed1, seed2) = split seed
(tail, seed3) = makeComponents seed2 cs
in
(c seed1 :: tail, seed3)The maximum value for randomly generated 32-bit ints.
The minimum value for randomly generated 32-bit ints.
A
Generatoris like a recipe for generating certain random values. So aGenerator Intdescribes how to generate integers and aGenerator Stringdescribes how to generate strings.