{-# LANGUAGE DeriveGeneric #-}
data Foo = A | B | C -- some generic data type
deriving Generic
Derive instances of Test.QuickCheck.Arbitrary.
instance Arbitrary Foo where
arbitrary = genericArbitraryuniform -- Give a distribution of constructors.
shrink = Test.QuickCheck.genericShrink -- Generic shrinking is provided by the QuickCheck library.
Or derive standalone generators (the fields must still be instances of
Test.QuickCheck.Arbitrary, or use custom generators).
Decrease size at every recursive call, but don't do anything different
at size 0.
genericArbitraryRec (7 % 11 % 13 % ()) :: Gen a
N.B.: This replaces the generator for fields of type [t] with
listOf' arbitrary instead of Test.QuickCheck.listOf arbitrary (i.e., arbitrary for
lists).
Decrease size to ensure termination for
recursive types, looking for base cases once the size reaches 0.
genericArbitrary' (17 % 19 % 23 % ()) :: Gen a
N.B.: This replaces the generator for fields of type [t] with
Test.QuickCheck.listOf' arbitrary instead of listOf arbitrary (i.e., arbitrary for
lists).
N.B.: This replaces the generator for fields of type [t] with
Test.QuickCheck.listOf' arbitrary instead of listOf arbitrary (i.e., arbitrary for
lists).
With custom generators
Note about incoherence
The custom generator feature relies on incoherent instances, which can
lead to surprising behaviors for parameterized types.
Example
For example, here is a pair type and a custom generator of Int (always
generating 0).
data Pair a b = Pair a b
deriving (Generic, Show)
customGen :: Gen Int
customGen = pure 0
The following two ways of defining a generator of Pair Int Int are
not equivalent.
The first way is to use genericArbitrarySingleG to define a
Gen (Pair a b) parameterized by types a and b, and then
specialize it to Gen (Pair Int Int).
In this case, the customGen will be ignored.
genPair :: (Arbitrary a, Arbitrary b) => Gen (Pair a b)
genPair = genericArbitrarySingleG customGen
genPair' :: Gen (Pair Int Int)
genPair' = genPair
-- Will generate nonzero pairs
The second way is to define Gen (Pair Int Int) directly using
genericArbitrarySingleG (as if we inlined genPair in genPair'
above.
Then the customGen will actually be used.
genPair2 :: Gen (Pair Int Int)
genPair2 = genericArbitrarySingleG customGen
-- Will only generate (Pair 0 0)
In other words, the decision of whether to use a custom generator
is done by comparing the type of the custom generator with the type of
the field only in the context where genericArbitrarySingleG is being
used (or any other variant with a G suffix).
In the first case above, those fields have types a and b, which are
not equal to Int (or rather, there is no available evidence that they
are equal to Int, even if they could be instantiated as Int later).
In the second case, they both actually have type Int.
Custom generators can be specified in a list constructed with (:+),
and passed to functions such as genericArbitraryG to override how certain
fields are generated.
Example:
customGens :: Gen String :+ Gen Int
customGens =
(filter (/= 'NUL') <$> arbitrary) :+
(getNonNegative <$> arbitrary)
There are also different types of generators, other than Test.QuickCheck.Gen, providing
more ways to select the fields the generator than by simply comparing types:
Test.QuickCheck.Gen a: override fields of type a;
Gen1 f: override fields of type f x for some x, requiring a generator for x;
Gen1_ f: override fields of type f x for some x, not requiring a generator for x;
FieldGen s a: override record fields named s, which must have type a;
ConstrGen c i a: override the field at index i of constructor c,
which must have type a (0-indexed);
Multiple generators may match a given field: the first, leftmost
generator in the list will be chosen.
Custom generators for "containers" of kind Type -> Type, parameterized
by the generator for "contained elements".
A custom generator Gen1 f will be used for any field whose type has the
form f x, requiring a generator of x. The generator for x will be
constructed using the list of custom generators if possible, otherwise
an instance Arbitrary x will be required.
An alternative to Test.QuickCheck.listOf that divides the size parameter
by the length of the list.
The length follows a geometric distribution of parameter
1/(sqrt size + 1).
An alternative to Test.QuickCheck.listOf1 (nonempty lists) that divides
the size parameter by the length of the list.
The length (minus one) follows a geometric distribution of parameter
1/(sqrt size + 1).
Note: it is recommended to avoid referring to the Options type
explicitly in code, as the set of options may change in the future.
Instead, use the provided synonyms (UnsizedOpts, SizedOpts, SizedOptsDef)
and the setter SetOptions (abbreviated as (<+)).
Whether to decrease the size parameter before generating fields.
The Sized option makes the size parameter decrease in the following way:
- Constructors with one field decrease the size parameter by 1 to generate
that field.
- Constructors with more than one field split the size parameter among all
fields; the size parameter is rounded down to then be divided equally.
Note: for recursive types which can recursively appear inside lists or other
containers, you may want to include a custom generator to decrease the size
when generating such containers.
See also the Note about lists in Generic.Random.Tutorial#notelists.
For custom generators to work with parameterized types, incoherent
instances must be used internally.
In practice, the resulting behavior is what users want 100% of the time,
so you should forget this option even exists.
Details
The default configuration of generic-random does a decent job if
we trust GHC implements precisely the instance resolution algorithm as
described in the GHC manual:
While that assumption holds in practice, it is overly context-dependent
(to know the context leading to a particular choice, we must replay the
whole resolution algorithm).
In particular, this algorithm may find one solution, but it is not
guaranteed to be unique: the behavior of the program is dependent on
implementation details.
An notable property to consider of an implicit type system (such as type
classes) is coherence: the behavior of the program is stable under
specialization.
This sounds nice on paper, but actually leads to surprising behavior for
generic implementations with parameterized types, such as generic-random.
To address that, the coherence property can be relaxd by users, by
explicitly allowing some custom generators to be chosen incoherently. With
appropriate precautions, it is possible to ensure a weaker property which
nevertheless helps keep type inference predictable: when a solution is
found, it is unique.
(This is assuredly weaker, i.e., is not stable under specialization.)
Decrease size at every recursive call, but don't do anything different
at size 0.
data X = ...
deriving Arbitrary via (GenericArbitraryRec '[2, 3, 5] X)
N.B.: This replaces the generator for fields of type [t] with
listOf' arbitrary instead of Test.QuickCheck.listOf arbitrary (i.e., arbitrary for
lists).
This newtype does no shrinking. To add generic shrinking, use AndShrinking.