In addition to the other great answers, I'd like to point out a few details.
First, in your process
function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1]
passes. Rather you need to pattern match like this:
... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)
Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:
prop_order :: [String] -> Bool
prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
prop_order _ = property ()
Also note that (if state then x <= y else False)
can be simplified to state && (x <= y)
.
Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property
instead of Bool
as the result type and using combinators in Test.QuickCheck.Property:
import Test.QuickCheck
import Test.QuickCheck.Property
-- | Tests if left is less or equal than right.
(<?=) :: (Ord a, Show a) => a -> a -> Property
x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)
prop_order :: [String] -> Property
prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
prop_order _ = property ()
Note that operators (.&.) and (.&&.) are very different!
I'd probably prefer yet another variant using conjoin
, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=
.
prop_order2 :: [String] -> Property
prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)