How do I generate random numbers, and make them reproducible, in EK9?

← Advanced Type System · Ref: Q1368

Randomness in EK9 is behind the Random trait, with two implementations - following the same injectable pattern as Clock / SystemClock.

THE RANDOM TRAIT

  next()                full-range Integer
  next(lower, upper)    Integer in the inclusive range [lower, upper]
  nextFloat()           Float in [0.0, 1.0)
  nextBoolean()         Boolean

TWO IMPLEMENTATIONS

  SystemRandom()        production - non-deterministic (a different sequence each run)
  SeededRandom(seed)    reproducible - a given seed yields a PINNED sequence, the same across runs and JVMs

WHY IT MATTERS

Because behaviour is behind a trait, code depends on Random and you inject the implementation. In production inject SystemRandom; in tests inject SeededRandom (or a mock) so anything using randomness becomes deterministic and its output can be asserted. Using SystemRandom in a test gives flaky, unrepeatable results - reach for SeededRandom there instead.

EXAMPLE (REPRODUCIBLE DICE)

  rnd <- SeededRandom(42)
  d1 <- rnd.next(1, 6)     // inclusive 1..6
  d2 <- rnd.next(1, 6)

The (lower, upper) bounds are inclusive and match OS().random(lower, upper).

Note there is no shuffle/choice primitive - write a small helper over next(l, u) if you need to reorder or pick from a List.

Clock / SystemClock is the sibling injectable pair. Use 'ek9 -h SeededRandom' for the full API.

Example

defines module qa.random.usage

  defines program

    RandomUsage()
      stdout <- Stdout()

      //Reproducible: the same seed yields the same sequence every run.
      rnd <- SeededRandom(42)

      d1 <- rnd.next(1, 6)
      d2 <- rnd.next(1, 6)
      stdout.println(`dice: ${d1} ${d2}`)

      f <- rnd.nextFloat()
      stdout.println(`float in [0,1): ${f}`)
Other ways to ask this
  • How do I seed a random number generator in EK9?
  • How do I make randomness deterministic in tests?
  • What is the difference between SystemRandom and SeededRandom?
  • How do I roll a dice or pick a random value?

Coming from another language?

Java: java.util.Random(seed) / ThreadLocalRandom. Python: random.seed / random.Random(seed). Go: math/rand with a seeded Source. Rust: rand crate with StdRng::seed_from_u64. EK9: a Random trait with SystemRandom (non-deterministic) and SeededRandom(seed) (reproducible), modelled on the injectable Clock/SystemClock pair; next()/next(l,u inclusive)/nextFloat/nextBoolean.

Keywords: trait, test, random, seed, seeded, seededrandom, reproducible, shuffle, inject, systemrandom, deterministic, dice, next