seeking lore of the QuickCheck masters

John Hughes rjmh@cs.chalmers.se
Tue, 29 Apr 2003 09:26:32 +0200 (MET DST)


On Tue, 15 Apr 2003, Malcolm Wallace wrote:

> nr@eecs.harvard.edu (Norman Ramsey) writes:
>
> > I am playing around with an implementation of a little language,
> > and I would like to use QuickCheck to test my code.  My problem is
> > that I want to test only well-typed terms.  I don't know how to craft
> > my `arbitrary' functions so that the probability of generating a well-typed
> > term is more than vanishingly small.  Can anybody pass on interesting
> > tips and tricks that might help?
>
> You want a generator that can produce an arbitrary code fragment,
> *given* what type you want it to have.  So first, generate an arbitrary
> type, then generate an arbitrary expression for it.
>
> Assuming that your little language is sort-of functional, then to
> generate a code fragment for a given type, say Int, you might have
> the following choices:
>
>  * Generate a literal value :: Int
>  * Generate a conditional.
>        e.g.  if <arbitrary :: Bool> then <arbitrary :: Int> else <arbitrary :: Int>
>  * Generate a case.
>        e.g.  case <arbitrary :: a> of
>                  <arbitrary pattern :: a>  ->  <arbitrary :: Int>
>                  <arbitrary pattern :: a>  ->  <arbitrary :: Int>
>  * and so on for other language constructs.
>  * Generate an application of a unary function
>                :: ArbitraryType a => a -> Int
>    or perhaps  :: ArbitraryTypeInvolving Int a => a -> Int
>    to a value of the constrained type.
>  * Generate an application of a binary function
>        :: (ArbitraryType a)                  => a -> (a -> Int)
>    or  :: (ArbitraryType a, ArbitraryType b) => a -> (b -> Int)
>    or  :: (ArbitraryTypeInvolving Int a)                  => a -> (a -> Int)
>    or  :: (ArbitraryTypeInvolving Int a, ArbitraryType b) => a -> (b -> Int)
>    or  :: (ArbitraryTypeInvolving Int b, ArbitraryType a) => a -> (b -> Int)
>    to a value of the constrained type.
>  * and so on for more complex types.
>
> It is quite a lot trickier than this little sketch, I'm sure, but I
> hope this helps to spark some more ideas.
>
> Regards,
>     Malcolm

Yes, this is how I would do it. Then, of course, it's important to control
the size of the generated terms. I would write a sized generator dividing
the size between sub-expressions, and restricting expressions to be
lambdas, constants or variables when the size reaches zero.

John