Home > Enterprise >  How to fix a matching error on a quickCheck done on a valid property of a function?
How to fix a matching error on a quickCheck done on a valid property of a function?

Time:10-06

I want to write a function search :: String -> Char -> [Int] that returns the positions of all occurrences of the second argument in the first. For example:

search "Bookshop" 'o' == [1,2,6]
search "senselessness" 's' == [0,3,7,8,11,12]

As per @Will Ness, the finalized code is as below:

search :: (Num b, Eq a, Enum b) => [a] -> a -> [b]
search s char = [ i | (i,c) <- zip [0..] s, c==char]

I want to do a QuickCheck test with a property of search, which is that the output cannot have more elements than the first argument. How will one achieve that? My skeleton code is as follows:

prop_search :: String -> Int -> Int -> Bool
prop_search x char = if length search x char <= length x then True else False

This generates a matching error where:

* Couldn't match expected type `Int -> Bool'
                  with actual type `Bool'
    * In the expression: False

Why is this so?

CodePudding user response:

There are several issues with prop_search. While the compiler messages in Haskell can be a bit daunting, the first troubleshooting tip is to realise that (unless you're using some advanced extensions to the language) the type annotations are redundant. The compiler doesn't need the type annotation, but it's good practice to include them because it helps with readability.

Thus, first try to see if the expression itself compiles. Here, I'm just using GHCi:

Prelude> prop_search x char = if length search x char <= length x then True else False

<interactive>:18:25: error:
    * Couldn't match expected type `t a -> t1 -> Int'
                  with actual type `Int'
    * The function `length' is applied to three arguments,
      but its type `([()] -> () -> [Integer]) -> Int' has only one
      In the first argument of `(<=)', namely `length search x char'
      In the expression: length search x char <= length x
    * Relevant bindings include
        char :: t1 (bound at <interactive>:18:15)
        x :: t a (bound at <interactive>:18:13)
        prop_search :: t a -> t1 -> Bool (bound at <interactive>:18:1)

So that doesn't work, but it's another error than the one posted in the OP. What does it say?

The function `length' is applied to three arguments

What's the type of length?

Prelude> :t length
length :: Foldable t => t a -> Int

length is a function that takes a single Foldable argument and returns an Int. It takes one, not three, arguments.

In the expression length search x char, however, it looks to the Haskell compiler as though length is being called with three arguments.

You want, instead, to measure the length of search x char, so group it:

prop_search x char = if length (search x char) <= length x then True else False

This now compiles, which means that you can inquire about its type:

Prelude> :t prop_search
prop_search :: Eq a => [a] -> a -> Bool

Notice that this isn't the type given in the OP. The function takes two arguments - not three.

You can copy that inferred type and add it as a type annotation to the function, if you want.

Finally, whenever you find yourself writing if x then True else False, you can instead just write x:

prop_search x char = length (search x char) <= length x

This still have the same type:

Prelude> :t prop_search
prop_search :: Eq a => [a] -> a -> Bool

In conclusion, you may want to write the function like this:

prop_search :: Eq a => [a] -> a -> Bool
prop_search x char = length (search x char) <= length x
  • Related