Search code examples
haskellinfinite-loopquickcheck

QuickCheck Generator not terminating


This QuickCheck generator

notEqual :: Gen (Int, Int)
notEqual = do
  (a, b) <- arbitrary
  if a /= b
    then return (a, b)
    else notEqual

Doesn't seem to terminate when passed to forAll. I'm guessing the recursion is going into an infinite loop somehow, but why?


Solution

  • The suchThat combinator that @Ashesh and @Carsten pointed out is definitely what I am looking for, to succinctly and idiomatically generate a non-equal pair.

    An explanation for the infinite recursion (Thanks to @oisdk):

    All QuckCheck runners (quickCheck, forAll etc.) pass a size parameter to test genarators. This has no defined semantics, but early tests use a small parameter, starting at 0*, and gradually growing. Generators use this to generate samples of different 'sizes,' whatever that may mean for a specific datatype.

    arbitrary for integral types (called recursively by arbitrary for (Int, Int)), uses this for the magnitude of the generated value - generate an integral between 0 and size.

    This means, unfortunately, that the first test attempted by quickCheck, (or in my case forAll,) uses the size 0, which can only generate (0, 0). This always fails the test of /=, causing the action to recurse infinitely, looking for more.

    * I'm assuming this, as the behaviour of the size parameter doesn't seem to be documented anywhere.