3
votes

I am trying to test my implementation of zipWith using QuickCheck. My implementation, myZipWith, I would like to QuickCheck test by comparing to the standard function. Something like:

main = do
  quickCheck (prop_myZipWith :: (Int -> Int -> Int) -> [Int] -> [Int] -> Bool)

prop_myZipWith :: (a -> b -> c) -> [a] -> [b] -> Bool
prop_myZipWith f x y = (myZipWith x y) == (zipWith f x y)

This does not work because (Int -> Int -> Int) is not an instance of Arbitrary.

With single-argument functions one can get around this using Test.QuickCheck.Function's Fun (which instantiates Arbitrary). For example:

main = do
  quickCheck (prop_myMap :: Fun Int Int -> [Int] -> Bool)

prop_myMap :: Fun a b -> [a] -> Bool
prop_myMap (Fun _ f) l = (myMap f l) == (map f l)

I am trying to do something similar except generating two-argument arbitrary functions.

How can I generate arbitrary instances of two argument functions for QuickCheck testing of higher-order functions such as zipWith?

1
Can't you just exploit currying and use Fun Int (Fun Int Int)? Remember that there's no such a thing as a binary function, since everything is curried.chi
Note that the statement "This does not work because (Int -> Int -> Int) is not an instance of Arbitrary." is false (functions are instances of Arbitrary). It doesn't work because functions aren't instances of Show. But as @chi says, currying solves your problem in a trivial way (and also in the only slightly less trivial way that Fun (Int, Int) Int would work as well). You may find the definitions unFun (Fun _ f) = f; unFun2 f = unFun . unFun f useful.user2407038
@chi, my limited experience in practice suggests that Fun (a,b,...) x is less painful to work with than Fun a (Fun b (... x ...))dfeuer
If your next question is "what about higher-order functions?", then that was mine too. But that appears to be Very Hard. Koen Claessen wasn't able to find a satisfactory solution, so you should only consider it if you want a research project.dfeuer

1 Answers

4
votes

QuickCheck 2.9.2

With QuickCheck 2.9.2, you can use the Fun type, and the Fn pattern. You'll have to

import Test.QuickCheck.Function

You can then write the property using a tuple as input, and then curry the function:

prop_myZipWith :: Eq c => Fun (a, b) c -> [a] -> [b] -> Bool
prop_myZipWith (Fn f) x y = myZipWith (curry f) x y == zipWith (curry f) x y

main looks like this:

main =
  quickCheck (prop_myZipWith :: Fun (Int, Int) Int -> [Int] -> [Int] -> Bool)

This compiles, and the test passes, in my repro.

QuickCheck 2.10.0.1

With QuickCheck 2.10.0.1, you can instead use the Fn2 pattern, like this:

prop_myZipWith :: Eq c => Fun (a, b) c -> [a] -> [b] -> Bool
prop_myZipWith (Fn2 f) x y = myZipWith f x y == zipWith f x y

The main method remains the same, because the type of prop_myZipWith hasn't changed, but import Test.QuickCheck.Function is no longer required.