Skip to content
You are reading v0.1, which is not the current release. Go to v0.4.

std::random

Numbers nobody chose.

A capability, not three free functions, and for a sharper reason than the usual one. “May read the environment” is worth seeing in a signature; “may generate randomness” is worth seeing and worth pinning, because a function that draws is a function whose test passes ninety-nine runs in a hundred. Random::seeded is the whole payoff: the same seed gives the same sequence, so a failure is reproducible and a fixture is a fixture rather than a coin flip. The permissions design note draws this line — the time, the environment and the dice are three different things to deny a dependency.

The generator is splitmix64, and it lives half here and half in the runtime: the state is one Int in a Shared, khora_random_step advances it and khora_random_mix turns a state into a draw. Splitting it that way is what keeps the critical section to a single addition — see draw below.

These are not cryptographic numbers. Anyone who sees one draw can predict every draw after it. Right for a shuffle, a retry jitter, a load-balancing choice or a test fixture; wrong for a session token or a key, which want a generator with a different name so that a program says which one it needed.

Effects: Random

Methods: Random

pub effect Random {
int: () -> Int,
in_range: (Int, Int) -> Int,
bytes: (Array<U8>) -> (),
}

A source of numbers nobody chose.

int: () -> Int

A number with all sixty-four bits drawn, which means it is negative about half the time. in_range is what a caller who wanted a positive number meant.

in_range: (Int, Int) -> Int

A number in [low, high) — low included, high not.

Half-open because that is the range that composes: in_range(0, n) indexes an array of n, and two adjacent ranges cover the space between them exactly once. An empty range stops the program, like an index outside an array does; there is no number in [low, low) to hand back, and returning low would make an off-by-one look like data.

bytes: (Array) ->()

Fills every element of the array with a drawn byte.

Into a buffer the caller owns rather than out of a returned one, because the caller is the one who knows how many bytes it wants and usually already has somewhere to put them — the same shape a socket read has.

impl Random
pub fn real() -> Random

The real thing, seeded by the operating system.

A different sequence every run, which is what production wants and exactly what a test does not. Note what this is not: a single generator the whole process shares. Each call seeds a new one, so two of them handed to two subsystems do not interleave — and a program that wants one shared source passes one Random around, which is what the capability is for.

pub fn seeded(seed: Int) -> Random

The same numbers every time, from the same seed.

The reason randomness is a capability. A test pins the sequence and gets an assertion that holds every run instead of a flake that shows up once a fortnight in someone else’s pull request. It also turns a failure in production into something reproducible: draw a seed from the real source, log it, hand it back here, and the run happens again.

The state is a Shared<Int> and that is the answer to fibers being threads. Two of them drawing at once are serialized by the cell’s lock — the same lock every other shared value uses, not a second mechanism — and the handler may cross into a fiber because a Shared is the one writable thing the sharing rules allow it to capture.

Khora 0.1.0built frombd964738afb6on 2026-10-17 20:41ZVersioningSecurityContributingReleasesSource