Quickcheck
Property testing: run a boolean property over many generated inputs and report the first counterexample, deterministically.
A Gen(a) is a seeded, sized function producing an a. It draws its randomness from the ambient Random effect, and the runner discharges that effect with a seeded SplitMix64 handler, so a whole run is a pure function of its seed. There is no real IO here: quickcheck performs no prim_rand, only the seeded stream, so a property that fails fails the same way on every backend and reruns identically from the reported seed. Determinism is the language’s contract, and a flaky property test would violate it.
The generator TYPE and its combinators live here; a derived Arbitrary(a) class (see Wire/deriving) supplies arbitrary : Gen(a) for user types by composing these combinators, so a derived instance plugs in with no rework.
Shrinking is intentionally omitted. The seam is the seed and size carried on a failing Outcome: a counterexample reproduces exactly via gen_at, and a future shrink : (a) -> List(a) hook would thread through check_go without changing this surface.
Types
Gen
type Gen(a) = Gen((Int) -> a ! {Random})
A seeded, sized generator of a. Apply it with gen_run; build one with the combinators below. The size bounds recursive shapes (list length, tree depth); make it explicit so derived instances for recursive types stay finite.
Config
type Config = Cfg { seed: U64, count: Int, max_size: Int }
How a property run configures the seeded stream: the base seed, the number of cases to try (count), and the largest size handed to a generator (max_size).
Outcome
type Outcome(a) = Passed(Int) | Failed(a, Int, U64, Int)
The result of a property run: Passed(n) after n cases held, or Failed(value, index, seed, size) with the first counterexample and the seed and size that reproduce it via gen_at.
Functions and Values
run_seeded
run_seeded : forall e0 a. (U64, () -> a ! {Random, e0}) -> a ! {e0}
Run action with the Random effect served by a seeded SplitMix64 stream, so the result is a pure function of seed. This is the REPLAYABLE handler the runner installs; call it directly to reproduce a single draw off a seed.
gen_run
gen_run : forall a. (Quickcheck.Gen(a), Int) -> a ! {Random}
Draw one value from g at the given size, performing Random.
gen_at
gen_at : forall a. (Quickcheck.Gen(a), U64, Int) -> a
Draw a value from g deterministically off seed at size, no IO. This is the reproduce-a-counterexample entry point.
gen_const
gen_const : forall a. (a) -> Quickcheck.Gen(a)
The generator that ignores size and randomness and always yields x.
gen_map
gen_map : forall a b. ((a) -> b, Quickcheck.Gen(a)) -> Quickcheck.Gen(b)
Map f over every value a generator produces.
gen_map2
gen_map2 : forall a b c. ((a, b) -> c, Quickcheck.Gen(a), Quickcheck.Gen(b)) -> Quickcheck.Gen(c)
Combine two generators with f, drawing both at the same size.
gen_bind
gen_bind : forall a b. (Quickcheck.Gen(a), (a) -> Quickcheck.Gen(b)) -> Quickcheck.Gen(b)
Monadic bind: draw an a, then draw from the generator f picks for it.
gen_sized
gen_sized : forall a. ((Int) -> Quickcheck.Gen(a)) -> Quickcheck.Gen(a)
Build a generator that sees the current size, for recursive shapes that branch on remaining fuel.
gen_resize
gen_resize : forall a. (Int, Quickcheck.Gen(a)) -> Quickcheck.Gen(a)
Run g at a fixed size, ignoring the ambient one (shrink a recursive position by resizing it smaller).
gen_choose
gen_choose : forall a. (Quickcheck.Gen(a), List(Quickcheck.Gen(a))) -> Quickcheck.Gen(a)
Pick one of g0/rest uniformly, then draw from it (one arm per constructor, the shape a derived sum-type instance uses).
gen_one_of
gen_one_of : forall a. (a, List(a)) -> Quickcheck.Gen(a)
Pick one of the given values uniformly (x0 or one of rest).
gen_int
gen_int : Quickcheck.Gen(Int)
Generator of Int, biased toward the edge cases (0, 1, -1, and a full-width draw) alongside small readable values.
gen_i64
gen_i64 : Quickcheck.Gen(I64)
Generator of I64, reusing the Int distribution.
gen_u64
gen_u64 : Quickcheck.Gen(U64)
Generator of U64, reusing the Int distribution (negatives wrap).
gen_bool
gen_bool : Quickcheck.Gen(Bool)
Generator of Bool.
gen_float
gen_float : Quickcheck.Gen(Float)
Generator of Float, including the nasty values (+/-0, +/-inf, NaN).
gen_char
gen_char : Quickcheck.Gen(Char)
Generator of a printable-ASCII Char.
gen_string
gen_string : Quickcheck.Gen(String)
Generator of a printable-ASCII String, length bounded by size.
gen_list
gen_list : forall a. (Quickcheck.Gen(a)) -> Quickcheck.Gen(List(a))
Generator of a List(a) whose length is bounded by size.
gen_option
gen_option : forall a. (Quickcheck.Gen(a)) -> Quickcheck.Gen(Option(a))
Generator of Option(a): None a quarter of the time, else Some.
gen_pair
gen_pair : forall a b. (Quickcheck.Gen(a), Quickcheck.Gen(b)) -> Quickcheck.Gen((a, b))
Generator of a pair, both drawn at the same size.
gen_triple
gen_triple : forall a b c. (Quickcheck.Gen(a), Quickcheck.Gen(b), Quickcheck.Gen(c)) -> Quickcheck.Gen((a, b, c))
Generator of a triple, all drawn at the same size.
default_config
default_config : Quickcheck.Config
The default configuration: a fixed base seed, 100 cases, sizes up to 20.
check_with
check_with : forall a. (Quickcheck.Config, Quickcheck.Gen(a), (a) -> Bool) -> Quickcheck.Outcome(a)
Run prop over cfg.count inputs from gen, returning the first counterexample or the count that passed. Deterministic in cfg.seed.
quickcheck
quickcheck : forall a. (Quickcheck.Gen(a), (a) -> Bool) -> Quickcheck.Outcome(a)
Run prop over the default configuration.
passed
passed : forall a. (Quickcheck.Outcome(a)) -> Bool
True when a run found no counterexample.
show_outcome
show_outcome : forall a. (String, Quickcheck.Outcome(a)) -> String
Render an outcome for name as one report line (pass) or a block naming the counterexample and the seed and size that reproduce it.