We can do some investigating by using verboseCheck
rather than quickCheck
.
*Main Test.QuickCheck> verboseCheck prop_sort6
Passed:
[]
Passed:
[]
Passed:
[(),()]
Passed:
[(),()]
Passed:
[(),(),(),()]
... (you get the picture) ...
quickCheck
(and verboseCheck
, by the same token) has the signature
quickCheck :: Testable prop => prop -> IO ()
Now, we can follow the rabbit hole down and look up what Testable
is, but the bottom line is that, whatever prop
is, it's going to have to be monomorphic at runtime. That is, it can't have any pesky lingering type variables. Now, the type of prop_sort6
is inferred to be
prop_sort6 :: Eq a => [a] -> Bool
So we need a good concrete a
which satisfies Eq
. For most typeclasses, this would be an ambiguous type error. If we wrote the following,
class Foo a
myProp :: Foo a => a -> Bool
myProp _ = True
Then quickCheck myProp
produces
<interactive>:29:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘quickCheck’
prevents the constraint ‘(Arbitrary a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b)
-- Defined in ‘Test.QuickCheck.Arbitrary’
instance Arbitrary Ordering
-- Defined in ‘Test.QuickCheck.Arbitrary’
instance Arbitrary Integer
-- Defined in ‘Test.QuickCheck.Arbitrary’
...plus 19 others
...plus 61 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: quickCheck myProp
In an equation for ‘it’: it = quickCheck myProp
<interactive>:29:12: error:
• No instance for (Foo a0) arising from a use of ‘myProp’
• In the first argument of ‘quickCheck’, namely ‘myProp’
In the expression: quickCheck myProp
In an equation for ‘it’: it = quickCheck myProp
However, Eq
is special. In GHCi (and only in GHCi), Eq
has type defaulting rules so that, in the absence of any additional information, Eq a
will be assumed to be Eq ()
(the unit type). This is only true in GHCi. If we make a main
function which calls quickCheck
, it's an ambiguous type error. However, in GHCi, it defaults to ()
.
Now, of course, ()
only has one instance, so we only end up testing the function with lists of ()
over and over again, and since your two functions will always produce lists of the same length, the test passes. You probably want to run it on Int
instead.
*Main Test.QuickCheck> quickCheck (prop_sort6 :: [Int] -> Bool)
*** Failed! Falsified (after 5 tests and 1 shrink):
[0,1]
Note that the compiler flag -Wtype-defaults
(enabled by -Wall
) will warn you about type defaulting and will let you know that something is amiss. With -Wtype-defaults
active:
*Main Test.QuickCheck> quickCheck prop_sort6
<interactive>:11:1: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘()’
(Arbitrary a0)
arising from a use of ‘quickCheck’ at <interactive>:11:1-21
(Show a0)
arising from a use of ‘quickCheck’ at <interactive>:11:1-21
(Eq a0)
arising from a use of ‘prop_sort6’ at <interactive>:11:12-21
• In the first argument of ‘GHC.GHCi.ghciStepIO ::
forall a. IO a -> IO a’, namely
‘(quickCheck prop_sort6)’
In a stmt of an interactive GHCi command:
it <- GHC.GHCi.ghciStepIO :: forall a. IO a -> IO a
(quickCheck prop_sort6)
+++ OK, passed 100 tests.
p == True
. This is always superfluous, equivalent top
all by itself. Specifically with QuickCheck, it also removes valuable information, namely what went wrong in case the equality is not fulfilled. You should writef1 xs === f2 xs
instead. – Deoxyribonuclease