Random
Random module: purely-functional pseudorandom number generation.
Algorithm: sfc32 ("Small Fast Counting", from PractRand) on 32-bit words. State: 4 × 32-bit non-negative integers packed into the Rng record (s0/s1/s2 = sfc32 a/b/c, s3 = the counter). Seeding: splitmix32-style hash expansion of the seed's base-2^32 digits, followed by a 12-round warm-up.
Why 32-bit: every intermediate value stays far below 2^53, so sequences are exact — and IDENTICAL — on the interpreter, native, and JS backends. (The previous 63-bit xoshiro256 core needed exact 63-bit wraparound, which the JS target's double-backed Int cannot represent, and its use of int_shr on negative words silently diverged between the interpreter's logical shift and native's arithmetic shift.)
Usage: let rng = Random.seed(42) let (x, rng2) = Random.next_int(rng) let (f, rng3) = Random.next_float(rng2)
All functions are pure: they take an Rng and return (value, new_Rng). No global mutable state.
Types
Functions
Sample from a Bernoulli distribution with success probability p: returns (true, new_rng) with probability p. p = 0.0 is always false and p = 1.0 always true. p must be in [0.0, 1.0]; panics otherwise.
Examples
let (heads, rng2) = Random.bernoulli(rng, 0.5)Pick a uniform element from a non-empty list. Returns (element, new_rng). Panics on empty input. O(length(xs)).
Examples
let (color, rng2) = Random.choice(rng, ["red", "green", "blue"])Weighted random selection from a list of (item, weight) pairs: each item is chosen with probability weight / total_weight, so zero-weight items are never chosen.
items must be non-empty. Weights must be non-negative and at least one must be positive (both data-dependent conditions the type system cannot express; still checked at runtime and panic if violated). Returns (chosen_item, new_rng). Linear in the number of items.
Examples
let items = [(:cat, 1.0), (:dog, 2.0), (:fish, 0.5)]
let (pet, rng2) = Random.choice_weighted(rng, items)
-- pet is one of :cat, :dog, :fish with probability ~0.28, 0.57, 0.14Sample from the exponential distribution with rate lambda > 0 (mean 1 / lambda). Uses the inverse-CDF method: if U ~ Uniform(0, 1) then X = -ln(U) / lambda is Exp(lambda). The result is always > 0. Panics if lambda <= 0. Returns (value, new_rng).
Examples
let (wait, rng2) = Random.exponential(rng, 0.5)Generate a random integer in [lo, hi] (inclusive), seeding from the current Unix time in whole seconds. Not reproducible, and two calls in the same second return the same value; thread an Rng through range instead when you need a sequence of values. Same lo <= hi requirement as range.
Examples
let n = Random.int(1, 100)Generate a pseudorandom float in [0.0, 1.0) (52 random bits divided by
- Returns
(value, new_rng).
Examples
let (f, rng2) = Random.next_float(Random.seed(7))
-- 0.0 <= f < 1.0Generate the next pseudorandom non-negative integer, uniform in [0, 2^52). 52 bits is the widest range that stays exact on every backend (the JS target stores Int in an IEEE double). Returns (value, new_rng).
Examples
let (n, rng2) = Random.next_int(Random.seed(1))
-- 0 <= n < 2^52; repeating with seed 1 gives the same nSample from the normal distribution N(mu, sigma^2). Panics if sigma < 0. sigma = 0 returns mu without consuming randomness (the returned Rng is the input Rng). Returns (value, new_rng).
Examples
let (height, rng2) = Random.normal(rng, 170.0, 10.0)Generate a random integer in [lo, hi] (inclusive). Spans up to 2^52. Requires lo <= hi: when hi == lo - 1 the modulus is zero and it panics (int_mod: division by zero); a smaller hi gives meaningless results. Returns (value, new_rng).
Examples
let (die, rng2) = Random.range(rng, 1, 6)
-- die is one of 1..6Sample n elements from xs without replacement (the first n of a shuffle). If n >= length(xs), returns all elements (shuffled); n <= 0 gives []. Returns (sample, new_rng). O(length(xs)^2).
Examples
let (picks, rng2) = Random.sample(rng, ["a", "b", "c", "d"], 2)
-- picks is two distinct elements of the listCreate a deterministic Rng from an integer seed. Any Int (including negative values) is accepted; the same seed yields the same sequence on every backend.
Examples
let rng = Random.seed(42)
let (n, rng2) = Random.next_int(rng)Shuffle a list using the Fisher-Yates algorithm. Returns (shuffled_list, new_rng). O(n^2) on lists. Same seed + same list always produces the same shuffle; the empty list shuffles to itself.
Examples
let (deck, rng2) = Random.shuffle(Random.seed(42), [1, 2, 3, 4, 5])
-- deck is a permutation of [1, 2, 3, 4, 5]Sample from the standard normal distribution N(0, 1) using the Box-Muller transform. Consumes two uniform samples per call and discards the second Gaussian deviate (simple + stateless; the pairing optimization requires a stateful RNG wrapper). Returns (value, new_rng).
Examples
let (z, rng2) = Random.standard_normal(rng)