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.
API at a glance
Section titled “API at a glance”Effects: Random
Methods: Random
Effects
Section titled “Effects”Random
Section titled “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
Section titled “in_range”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.
Methods
Section titled “Methods”Random
Section titled “Random”impl Randompub fn real() -> RandomThe 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.
seeded
Section titled “seeded”pub fn seeded(seed: Int) -> RandomThe 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.3.0built frombd964738afb6on 2026-10-17 20:41ZVersioningSecurityContributingReleasesSource