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 step 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 over time, you should store the most recent seed in your model. If
you have many separate models, you can give them all independent
seeds.
This is an implementation of PCG by M. E. O'Neil, and is not cryptographically secure.
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.
(x, seed1) = step (int 0 100) seed0
(y, seed2) = step (int 0 100) seed1
(z, seed3) = step (int 0 100) seed2
[x, y, z] -- [85, 0, 38]Notice that we use different seeds on each line. This is important! If you reuse the same seed, you get the same results.
(x, _) = step (int 0 100) seed0
(y, _) = step (int 0 100) seed0
(z, _) = step (int 0 100) seed0
[x,y,z] -- [85, 85, 85]As you can see, threading seeds through many calls to step is tedious and
error-prone. That's why this library includes many functions to build more
complicated generators, allowing you to call step only a small number of
times.
Our example is best written as:
(xs, newSeed) = step (list 3 <| int 0 100) seed0
xs -- [85, 0, 38]A Generator is like a recipe for generating certain random values. So a
Generator Int describes how to generate integers and a Generator String
describes how to generate strings.
Create a generator that produces boolean values with equal probability. This example simulates flipping three coins and checking if they're all heads.
threeHeads : Generator Bool
threeHeads =
map3 (\a b c -> a && b && c) bool bool boolGenerate 32-bit integers in a given range, inclusive.
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.
Performance note: This function will be ~1.5x faster if the range (i.e. max - min + 1) is a power of two. The
effect will only be noticable if you are generating tens of thousands of random integers.
Generate floats in a given range. The following example is a generator that produces numbers between 0 and 1.
probability : Generator Float
probability =
float 0 1Produce True one-in-n times on average.
Do not pass a value less then one to this function.
flippedHeads = oneIn 2
rolled6 = oneIn 6Given a list, choose an element uniformly at random. Nothing is only
produced if the list is empty.
type Direction = North | South | East | West
direction : Generator Direction
direction =
sample [North, South, East, West]
|> map (Maybe.withDefault North)Create 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)Produce Just a value on True, and Nothing on False.
You can use bool or oneIn n for the first argument.
Choose between two values with equal probability.
type Flip = Heads | Tails
coinFlip : Generator Flip
coinFlip =
choice Heads TailsCreate a generator that always produces the value provided. This is useful when creating complicated chained generators and you need to handle a simple case. It's also useful for the base case of recursive generators.
Transform the values produced by a generator using a stateless function as a callback.
These examples show how to generate letters based on a basic integer generator.
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 is useful when you have a function with two arguments that both need to be given random inputs.
pointInCircle : Float -> Generator (Float, Float)
pointInCircle radius =
let
r = float 0 radius
theta = map degrees (float 0 360)
in
map2 (curry fromPolar) r thetaCombine three generators. This could be used to produce random colors.
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 (float 0 360)) (float 0 1) (float 0 1)Combine four generators. This could be used to produce random transparent colors.
rgba : Generator Color.Color
rgba =
map4 Color.rgba (int 0 255) (int 0 255) (int 0 255) (float 0 1)Combine five generators.
Map over any number of generators.
randomPerson : Generator Person
randomPerson =
person `map` genFirstName
`andMap` genLastName
`andMap` genBirthday
`andMap` genPhoneNumber
`andMap` genAddress
`andMap` genEmailChain random operations by providing a callback that accepts a randomly-generated value. The random value can be used to drive more randomness.
The argument order matches andThens from core, but requires the use of flip
to match map or work with |> chains.
This example shows how we can use andThen to generate a list of random values
and random length. Then we use map to apply a stateless function to that
list. Assume we already have genName : Generator String defined.
authors : Generator String
authors =
int 1 5 -- number of authors
|> (flip andThen) (\i -> list i genName)
|> map (\ns ->
case ns of
[n] ->
"Author: " ++ n
n::ns ->
"Authors: " ++ String.join ", " ns ++ " and " ++ n
[] ->
"This can't happen"
)If you find yourself calling constant in every branch of the callback, you can
probably use map instead.
Filter a generator so that all generated values satisfy the given predicate.
evens : Generator Int
evens =
filter (\i -> i % 2 == 0) (int minInt maxInt)If the predicate is unsatisfiable, the generator will not terminate, your application will crash with a stack overflow, and you will be sad. You should also avoid predicates that are merely very difficult to satisfy.
badCrashingGenerator =
filter (\_ -> False) anotherGenerator
likelyCrashingGenerator =
filter (\i -> i % 2000 == 0) (int minInt maxInt)A Seed is the source of randomness in the whole system. It hides the
current state of the random number generator.
Generators, not seeds, are the primary data structure for generating random values. Generators are much easier to chain and combine than functions that take and return seeds. Creating and managing seeds should happen "high up" in your program.
Like initialSeed2, but takes only one integer. Mostly for compatibility
with core.
A generator that produces a seed that is independent of any other seed in the program. These seeds will generate their own unqiue sequences of random values. They are useful when you need an unknown amount of randomness later but can request only a fixed amount of randomness now.
Let's say you write a component that uses some randomness to initialize itself
and then never needs randomness again. You can easily write a Generator
Component by mapping over the generators it needs. But if component requires
randomness after initialization, it should keep its own independent seed, which
it can get by mapping over this generator.
type alias Component = { seed : Seed }
genComponent : Generator Component
genComponent = map Component independentSeedIf you have a lot of components, you can initialize them like so:
genComponents : List (Seed -> a) -> Generator (List a)
genComponents constructors =
list (List.length constructors) independentSeed
|> map (List.map2 (<|) constructors)The independent seeds are extremely likely to be distinct for all practical purposes. However, it is not proven that there are no pathological cases.
Fast forward a seed the given number of steps, which may be negative (the
seed will be "rewound"). This allows a single seed to serve as a random-access
lookup table of random numbers. (To be sure no one else uses the seed, use
step independentSeed to split off your own.)
diceRollTable : Int -> Int
diceRollTable i =
fastForward i mySeed |> step (int 1 6) |> fstSerialize a seed as a JSON value
to be sent out a port, stored in local storage, and so on. The seed can be
recovered using fromJson.
Do not inspect or change the resulting JSON value.
A JSON decoder that can recover seeds encoded using toJson.
Json.Decode.decodeValue fromJson (toJson mySeed) == Ok mySeedIf the JSON is an array of one or two integers, or just an integer, these will be used to initialize a new seed. This can be useful when you sometimes have an old seed and sometimes need a new one. The integers should be 32 random bits.
The minimum value for randomly generated 32-bit ints.
The maximum value for randomly generated 32-bit ints.
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, and those bits should be as random as possible.
You can generate and copy random integers to create a reproducible psuedo-random generator.
Alternatively, you can generate the random integers on page load and pass them through a port. The program will be different every time.
Either way, you should initialize a random seed only once. After that, whenever you use a seed, you'll get another one back.