[Haskell-cafe] Co-arbitrary

Malcolm Wallace Malcolm.Wallace at cs.york.ac.uk
Tue May 8 04:48:39 EDT 2007


Joel Reymont <joelr1 at gmail.com> writes:

> Would someone kindly explain why we need co-arbitrary in QuickCheck  
> and how to define it?

Ever written a higher-order function and wanted to quickcheck it?
So where do you get your random functional arguments from?

Whereas the 'arbitrary' method allows you to write a generator for
random values of a constructed datatype, the 'coarbitrary' method allows
you to write a generator for random *functions* that take a
randomly-generated value as argument, and produce a randomly-generated
result that is nevertheless determined by / dependent on the argument.

> Detailed examples would be awesome!

There are lots of instances already defined in Test.QuickCheck that are
worth reading.  In almost all cases they use the generator-combinator
'variant', which is like 'coarbitrary' but selects its result based on
an Int rather than a value of an arbitrary datatype.  So in most cases,
implementing 'coarbitrary' amounts to converting the given argument to
an appropriate Int to pass to 'variant'.

    instance Arbitrary Bool where
        coarbitrary b = if b then variant 0 else variant 1

The result type of 'corarbitrary' (and 'variant') :: Gen b -> Gen b
means that, once some other part of the quickcheck apparatus has
determined what the result type of the generated function should be
(i.e. b), then given that we already have a random generator for one of
those result values, what we are doing is *modifying* that
result-generator to be dependent on the function's argument rather than
entirely arbitrary.

So in the Bool instance, 'coarbitrary' gives you a function-generator
that picks a 0-based result-generator in the False case, or a 1-based
result-generator in the True case.

To write your own 'coarbitrary', you are aiming to convert values of
your datatype into a disjoint partition of the set of all Ints.  In
the end it is rather mechanical: e.g.

    data Foo a = Foo String a
               | Bar Bool

    instance Arbitrary (Foo a) where
        coarbitrary (Foo s a) = variant 0 . coarbitrary s . coarbitrary a
        coarbitrary (Bar b)   = variant 1 . coarbitrary b

Regards,
    Malcolm


More information about the Haskell-Cafe mailing list