简体   繁体   中英

Anything wrong with the type declaration?

I am reading the book "Programming in Haskell". One exercise asks me to define map f using an higher-order function. I choose to define map (+1) like the following:

unfold p h t x | p x       = []
               | otherwise = h x : unfold p h t (t x)

-- equivalent to `map (+1)`
mapinc = unfold (==[]) ((+1).head) tail

(Taken straight from the exercise question) The function unfold pht produces the empty list if the predicate p is true of the argument value, and otherwise produces a non-empty list by applying the function h to this value to give the head, and the function t to generate another argument that is recursively processed in the same way to produce the tail of the list.

I have checked my implementation of mapinc and it looks fine:

*Main> mapinc [1,2,3]
[2,3,4]

However, after I add the type declaration:

mapinc :: Num a => [a] -> [a]
mapinc = unfold (==[]) ((+1).head) tail

Then reload the script in WinGHCi, it gives the following error:

• Could not deduce (Eq a) arising from a use of ‘==’
  from the context: Num a
    bound by the type signature for:
               mapinc :: forall a. Num a => [a] -> [a]
    at D:\7a.hs:4:1-29
  Possible fix:
    add (Eq a) to the context of
      the type signature for:
        mapinc :: forall a. Num a => [a] -> [a]
• In the first argument of ‘unfold’, namely ‘(== [])’
  In the expression: unfold (== []) ((+ 1) . head) tail
  In an equation for ‘mapinc’:
      mapinc = unfold (== []) ((+ 1) . head) tail
  |
5 | mapinc = unfold (==[]) ((+1).head) tail   |                  ^^^^

Any clue why it happens?

Your signature is too broad . The predicate you have written is == [] . Haskell can only check if two lists are equal, if the elements of the list can be checked as well. In the source code we see something like:

instance  Eq [a] where
    ...

Yes, here we will never check the equality of two items, since we check with the empty list, but the compiler of course does not know that: it simply sees that in order to check whether two lists are equal, we need to be able to check if elements are equal.

The Num typeclass does not imply that the type is also an Eq type. We can do two things here:

  1. add the Eq type constraint to the signature:

     mapinc ::  Num a  => [a] -> [a] mapinc = unfold (==[]) ((+1).head) tail 
  2. more elegant: do not rely on the fact that we need to be able to compare elements, but use null :: [a] -> Bool instead (a function that checks if the list is empty):

     mapinc :: Num a => [a] -> [a] mapinc = unfold  ((+1).head) tail 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM