An Arbitrary instance tells QuickCheck how to create test inputs for a type and how to reduce failing inputs to simpler candidates. Its required method is arbitrary :: Gen a; shrink :: a -> [a] is optional at first, but makes failing property tests easier to inspect. Generator selection is driven by types, so a polymorphic generator can reuse existing instances for its component types. section 9.4 section 9.6
ghci> :i Arbitrary
type Arbitrary :: * -> Constraint
class Arbitrary a where
arbitrary :: Gen a
shrink :: a -> [a]
{-# MINIMAL arbitrary #-}
The class interface separates random generation from deterministic shrinking. Implement arbitrary to make a type generatable; add shrink to improve counterexamples. section 9.4
Build generators with Gen combinators and do notation. arbitrary supplies values from an available instance, while choose, elements, vectorOf, and suchThat shape them into valid test data. Size can bound complexity: an alphabet-rotation generator chooses a nonempty alphabet, a size-related rotation amount, and a character from that alphabet. section 9.4.1
Preserving an AssocMap invariant
For an AssocMap backed by [(k, v)], generate arbitrary keys, remove duplicates, generate an equal number of arbitrary values, and zip them together. The instance needs Eq k to enforce unique keys plus Arbitrary k and Arbitrary v to obtain component values. Its shrinker can shrink the backing list, then remove duplicate keys again so every candidate remains a valid map. section 9.4.2 section 9.4.3
A useful Arbitrary instance follows two rules:
- Generate values that satisfy the type’s invariants rather than merely matching its representation. section 9.4.2
- Return simpler deterministic shrink candidates without returning the original value, or shrinking can loop. section 9.4.3
- Recheck each shrink candidate against the failing property until no smaller falsifying input remains. section 9.4.3
Shrinking is not another random generation pass. Given a failing value, shrink produces a predetermined list of alternatives, and QuickCheck tests those alternatives to locate a minimal counterexample. A custom shrinker must preserve the same invariants as the generator, since a malformed candidate can obscure the failure being diagnosed. section 9.4.3
