Random

This library helps you generate pseudo-random values.

It is an implementation of Permuted Congruential Generators by M. E. O'Neil. It is not cryptographically secure.

Generators

type Generator a

A Generator is a recipe for generating random values. For example, here is a generator for numbers between 1 and 10 inclusive:

import Random

oneToTen : Random.Generator Int
oneToTen =
  Random.int 1 10

Notice that we are not actually generating any numbers yet! We are describing what kind of values we want. To actually get random values, you create a command with the generate function:

type Msg = NewNumber Int

newNumber : Cmd Msg
newNumber =
  Random.generate NewNumber oneToTen

Each time you run this command, it runs the oneToTen generator and produces random integers between one and ten.

Note 1: If you are not familiar with commands yet, start working through guide.elm-lang.org. It builds up to an example that generates random numbers. Commands are one of the core concepts in Gren, and it will be faster overall to invest in understanding them now rather than copy/pasting your way to frustration! And if you feel stuck on something, definitely ask about it in the Gren slack. Folks are happy to help!

Note 2: The random Generator API is quite similar to the JSON Decoder API. Both are building blocks that snap together with map, map2, etc. You can read more about JSON decoders here to see the similarity.

generate : (a -> msg) -> Generator a -> Cmd msg

Create a command that produces random values. Say you want to generate random points:

import Random

point : Random.Generator { first : Int, second : Int }
point =
  Random.pair (Random.int -100 100) (Random.int -100 100)

type Msg = NewPoint { first : Int, second : Int }

newPoint : Cmd Msg
newPoint =
  Random.generate NewPoint point

Each time you run the newPoint command, it will produce a new 2D point like { first = 57, second = 18 } or { first = -82, second = 6 }.

Note: Read through guide.elm-lang.org to learn how commands work. If you are coming from JS it can be hopelessly frustrating if you just try to wing it. And definitely ask around on Slack if you feel stuck! Investing in understanding generators is really worth it, and once it clicks, folks often dread going back to Math.random() in JavaScript.

Primitives

int : Int -> Int -> Generator Int

Generate 32-bit integers in a given range.

import Random

singleDigit : Random.Generator Int
singleDigit =
    Random.int 0 9

closeToZero : Random.Generator Int
closeToZero =
    Random.int -5 5

anyInt : Random.Generator Int
anyInt =
    Random.int Random.minInt Random.maxInt

This generator can produce values outside of the range [minInt, maxInt] but sufficient randomness is not guaranteed.

float : Float -> Float -> Generator Float

Generate floats in a given range.

import Random

probability : Random.Generator Float
probability =
    Random.float 0 1

The probability generator will produce values between zero and one with a uniform distribution. Say it produces a value p. We can then check if p < 0.4 if we want something to happen 40% of the time.

This becomes very powerful when paired with functions like map and andThen. Rather than dealing with twenty random float messages in your update, you can build up sophisticated logic in the Generator itself!

uniform : a -> Array a -> Generator a

Generate values with equal probability. Say we want a random suit for some cards:

import Random

type Suit = Diamond | Club | Heart | Spade

suit : Random.Generator Suit
suit =
  Random.uniform Diamond [ Club, Heart, Spade ]

That generator produces all Suit values with equal probability, 25% each.

Note: Why not have uniform : Array a -> Generator a as the API? It looks a little prettier in code, but it leads to an awkward question. What do you do with uniform []? How can it produce an Int or Float? The current API guarantees that we always have at least one value, so we never run into that question!

weighted :
{ weight : Float
, value : a
}
-> Array { weight : Float, value : a }
-> Generator a

Generate values with a weighted probability. Say we want to simulate a loaded die that lands on ⚄ and ⚅ more often than the other faces:

import Random

type Face = One | Two | Three | Four | Five | Six

roll : Random.Generator Face
roll =
  Random.weighted
    { weight : 10, value : One }
    [ { weight : 10, value : Two }
    , { weight : 10, value : Three }
    , { weight : 10, value : Four }
    , { weight : 20, value : Five }
    , { weight : 40, value : Six }
    ]

So there is a 40% chance of getting Six, a 20% chance of getting Five, and then a 10% chance for each of the remaining faces.

Note: I made the weights add up to 100, but that is not necessary. I always add up your weights into a total, and from there, the probablity of each case is weight / total. Negative weights do not really make sense, so I just flip them to be positive.

constant : a -> Generator a

Generate the same value every time.

import Random

alwaysFour : Random.Generator Int
alwaysFour =
    Random.constant 4

Think of it as picking from a hat with only one thing in it. It is weird, but it can be useful with elm-community/random-extra which has tons of nice helpers.

Data Structures

pair :
Generator a
-> Generator b
-> Generator { first : a, second : b
}

Generate a pair of random values. A common use of this might be to generate a point in a certain 2D space:

import Random

randomPoint : Random.Generator { first : Float, second : Float }
randomPoint =
    Random.pair (Random.float -200 200) (Random.float -100 100)

Maybe you are doing an animation with SVG and want to randomly generate some entities?

array : Int -> Generator a -> Generator (Array a)

Generate an array of random values.

import Random

tenFractions : Random.Generator (Array Float)
tenFractions =
    Random.array 10 (Random.float 0 1)

fiveGrades : Random.Generator (Array Int)
fiveGrades =
    Random.array 5 (int 0 100)

If you want to generate an array with a random length, you need to use andThen like this:

fiveToTenDigits : Random.Generator (Array Int)
fiveToTenDigits =
    Random.int 5 10
      |> Random.andThen (\len -> Random.array len (Random.int 0 9))

This generator gets a random integer between five and ten and then uses that to generate a random array of digits.

Mapping

map : (a -> b) -> Generator a -> Generator b

Transform the values produced by a generator. For example, we can generate random boolean values:

import Random

bool : Random.Generator Bool
bool =
  Random.map (\n -> n < 20) (Random.int 1 100)

The bool generator first picks a number between 1 and 100. From there it checks if the number is less than twenty. So the resulting Bool will be True about 20% of the time.

You could also do this for lower case ASCII letters:

letter : Random.Generator Char
letter =
  Random.map (\n -> Char.fromCode (n + 97)) (Random.int 0 25)

The letter generator first picks a number between 0 and 25 inclusive. It then uses Char.fromCode to turn ASCII codes into Char values.

Note: Instead of making these yourself, always check if the random-extra package has what you need!

map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c

Combine two generators. Maybe we have a space invaders game and want to generate enemy ships with a random location:

import Random

type alias Enemy =
  { health : Float
  , rotation : Float
  , x : Float
  , y : Float
  }

enemy : Random.Generator Enemy
enemy =
  Random.map2
    (\x y ->
      { health = 100
      , rotation = 0
      , x = x
      , y = y
      }
    )
    (Random.float 0 100)
    (Random.float 0 100)

Now whenever we run the enemy generator we get an enemy with full health, no rotation, and a random position! Now say we want to start with between five and ten enemies on screen:

initialEnemies : Random.Generator (Array Enemy)
initialEnemies =
  Random.int 5 10
    |> Random.andThen (\num -> Random.array num enemy)

We will generate a number between five and ten, and then generate that number of enemies!

Note: Snapping generators together like this is very important! Always start by with generators for each type you need, and then snap them together.

map3 :
(a -> b -> c -> d)
-> Generator a
-> Generator b
-> Generator c
-> Generator d

Combine three generators. Maybe you want to make a simple slot machine?

import Random

type alias Spin =
  { one : Symbol
  , two : Symbol
  , three : Symbol
  }

type Symbol = Cherry | Seven | Bar | Grapes

spin : Random.Generator Spin
spin =
  Random.map3 (\one two three -> { one = one, two = two, three = three })
    symbol
    symbol
    symbol

symbol : Random.Generator Symbol
symbol =
  Random.uniform Cherry [ Seven, Bar, Grapes ]

Note: Always start with the types. Make a generator for each thing you need and then put them all together with one of these map functions.

map4 :
(a -> b -> c -> d -> e)
-> Generator a
-> Generator b
-> Generator c
-> Generator d
-> Generator e

Combine four generators.

Say you are making game and want to place enemies or terrain randomly. You could generate a quadtree!

import Random

type QuadTree a
  = Empty
  | Leaf a
  | Node (QuadTree a) (QuadTree a) (QuadTree a) (QuadTree a)

quadTree : Random.Generator a -> Random.Generator (QuadTree a)
quadTree leafGen =
  let
    subQuads =
      Random.lazy (\_ -> quadTree leafGen)
  in
  Random.andThen identity <|
    Random.uniform
      (Random.constant Empty)
      [ Random.map Leaf leafGen
      , Random.map4 Node subQuad subQuad subQuad subQuad
      ]

We start by creating a QuadTree type where each quadrant is either Empty, a Leaf containing something interesting, or a Node with four sub-quadrants.

Next the quadTree definition describes how to generate these values. A third of a time you get an Empty tree. A third of the time you get a Leaf with some interesting value. And a third of the time you get a Node full of more QuadTree values. How are those subtrees generated though? Well, we use our quadTree generator!

Exercises: Can quadTree generate infinite QuadTree values? Is there some way to limit the depth of the QuadTree? Can you render the QuadTree to HTML using absolute positions and fractional dimensions? Can you render the QuadTree to SVG?

Note: Check out the docs for lazy to learn why that is needed to define a recursive Generator like this one.

map5 :
(a -> b -> c -> d -> e -> f)
-> Generator a
-> Generator b
-> Generator c
-> Generator d
-> Generator e
-> Generator f

Combine five generators.

If you need to combine more things, you can always do it by chaining andThen. There are also some additional helpers for this in elm-community/random-extra.

Fancy Stuff

andThen : (a -> Generator b) -> Generator a -> Generator b

Generate fancy random values.

We have seen examples of how andThen can be used to generate variable length arrays in the array and map2 docs. We saw how it could help generate a quadtree in the map4 docs.

Anything you could ever want can be defined using this operator! As one last example, here is how you can define map using andThen:

import Random

map : (a -> b) -> Random.Generator a -> Random.Generator b
map func generator =
  generator
    |> Random.andThen (\value -> Random.constant (func value))

The andThen function gets used a lot in elm-community/random-extra, so it may be helpful to look through the implementation there for more examples.

lazy : ({} -> Generator a) -> Generator a

Helper for defining self-recursive generators. Say we want to generate a random number of probabilities:

import Random

probabilities : Random.Generator (Array Float)
probabilities =
  Random.andThen identity <|
    Random.uniform
      (Random.constant [])
      [ Random.map2 (::)
          (Random.float 0 1)
          (Random.lazy (\_ -> probabilities))
      ]

In 50% of cases we end the array. In 50% of cases we generate a probability and add it onto a random number of probabilities. The lazy call is crucial because it means we do not unroll the generator unless we need to.

This is a pretty subtle issue, so I recommend reading more about it here!

Note: You can delay evaluation with andThen too. The thing that matters is that you have a function call that delays the creation of the generator!

Constants

maxInt : Int

The underlying algorithm works well in a specific range of integers. It can generate values outside of that range, but they are “not as random”.

The maxInt that works well is 2147483647.

minInt : Int

The underlying algorithm works well in a specific range of integers. It can generate values outside of that range, but they are “not as random”.

The minInt that works well is -2147483648.

Generate Values Manually

type Seed

Maybe you do not want to use generate for some reason? Maybe you need to be able to exactly reproduce a sequence of random values?

In that case, you can work with a Seed of randomness and step it forward by hand.

step : Generator a -> Seed -> { value : a, seed : Seed }

So you need reproducible randomness for some reason.

This step function lets you use a Generator without commands. It is a normal Gren function. Same input, same output! So to get a 3D point you could say:

import Random

type alias Point3D = { x : Float, y : Float, z : Float }

makePoint3D : Float -> Float -> Float -> Point3D
makePoint3D x y z =
  { x = x, y = y, z = z }

point3D : Random.Seed -> { value : Point3D, seed : Random.Seed }
point3D seed0 =
  let
    { value = x, seed = seed1 } = Random.step (Random.float 0 100) seed0
    { value = y, seed = seed2 } = Random.step (Random.float 0 100) seed1
    { value = z, seed = seed3 } = Random.step (Random.float 0 100) seed2
  in
    { value = makePoint3D x y z, seed = seed3 }

Notice that we use different seeds on each line! If we instead used seed0 for everything, the x, y, and z values would always be exactly the same! Same input, same output!

Threading seeds around is not super fun, so if you really need this, it is best to build your Generator like normal and then just step it all at once at the top of your program.

initialSeed : Int -> Seed

Create a Seed for reproducible randomness.

import Random

seed0 : Random.Seed
seed0 =
  Random.initialSeed 42

If you hard-code your Seed like this, every run will be the same. This can be useful if you are testing a game with randomness and want it to be easy to reproduce past games.

In practice, you may want to get the initial seed by (1) sending it to Gren through flags or (2) using Time.now to get a number that the user has not seen before. (Flags are described on this page.)

independentSeed : Generator Seed

A generator that produces a seed that is independent of any other seed in the program. These seeds will generate their own unique 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.

The independent seeds are extremely likely to be distinct for all practical purposes. However, it is not proven that there are no pathological cases.