I'm trying to learn QuickCheck and understand how it works. The data type of the quickCheck function is
quickCheck :: Testable prop => prop -> IO ()
On the other hand I have a function
prop_rev_involutive:Eq a => [a] -> Bool
prop_rev_involutive l = (reverse $ reverse l) == l
that I'm using for input for quickCheck. Running quickCheck prop_rev_involutive
works fine but I don't understand how the types are matching.
is the type [a] -> Bool
considered to be Testable
? Why is this?
Testable
is a type class - essentially an abstraction over things that can be turned into an automated test. The type class has several instances, one of which is Bool
:
Testable Bool
That's not the type of prop_rev_involutive
, though. Another instance of Testable
is:
(Arbitrary a, Show a, Testable prop) => Testable (a -> prop)
This declares that for any a
which is an instance of both Arbitrary
and Show
, a function from a
to prop
is itself Testable
. We've already established that Bool
is a Testable
instance, so now you need to investigate whether [a]
is an Arbitrary
and Show
instance.
The Arbitrary
type class has a ton of instances, one of which is:
Arbitrary a => Arbitrary [a]
This declares that if a
is an Arbitrary
instance, then so is [a]
. So, is a
an Arbitrary
instance? That depends on which concrete type you actually run quickCheck
with. Some Haskell environments default to certain types (e.g. Int
, which is an Arbitrary
instance).
You'll need to go through the same line of reasoning for the Show
type class.
I don't think that any unconstrained function [a] -> Bool
is a Testable
instance, but something like (Arbitrary a, Show a) => [a] -> Bool
is. Examples include [Int] -> Bool
and [Char] -> Bool
. This is because Bool
is Testable
, and Int
and Char
are both Arbitrary
and Show
instances.