Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.