views:

196

answers:

4

I'm trying to compile the following function in Haskell to mimic differentiation of a polynomial whose constants are specified in a numerical list:

diff :: (Num a) => [a] -> [a]
diff [] = error "Polynomial unspecified"
diff coeff = zipWith (*) (tail coeff) [0..]

Haskell refuses to compile it, giving me the following reason:

Could not deduce (Enum a) from the context (Num a)
 arising from the arithmetic sequence `0 .. ' at fp1.hs:7:38-42
Possible fix:
 add (Enum a) to the context of the type signature for `diff'
In the third argument of `zipWith', namely `[0 .. ]'
In the expression: zipWith (*) (tail coeff) ([0 .. ])
In the definition of `diff':
diff coeff = zipWith (*) (tail coeff) ([0 .. ])

Why is Haskell treating the [0..] list as an Enum type, and how can I fix this. Bear in mind that I want to take advantage of lazy evaluation here, hence the infinite list.

+5  A: 

The correct type of diff has to be

diff :: (Num a, Enum a) => [a] -> [a]

because the usage of [x..] requires the type to instantiate Enum.

Dario
+2  A: 

[0..] is shorthand for enumFrom 0 See here

newacct
+7  A: 

[0..] is syntactic sugar for enumFrom 0, defined in class Enum. Because you want to generate a list of as with [0..] the compiler demands a to be in class Enum.

You can either add the Enum a to the type signature of the function or work around it by generating a [0..] :: [Integer] and using fromInteger (which is defined in class Num) to get a [a] from that:

diff :: (Num a) => [a] -> [a]
diff [] = error "Polynomial unspecified"
diff coeff = zipWith (*) (tail coeff) (map fromInteger [0..])
sth
Hmm, I'd probably write `(iterate (+1) 0)` as it's shorter, but in the end it's all the same...
ephemient
Interesting. I thought it was just an infinite list of integers. The things you learn...
Zaid
The funny thing is that the whole problem could have been solved by simply omitting the type signature and let the inference machine do its work.
Dario
You can also do things like `['a'..'z']` or `[1.0, 1.1 .. 2.0]` since `Char` and `Float` have `Enum` instances...
sth
@Dario: I tried that and it didn't work for me.
Zaid
+2  A: 

Here's a quick summary of what the compiler sees when it looks at this function:

  • [0..] is a list of things that have both Num and Enum instances. It has to be a Num because of the '0', and it has to be an Enum because of the '..'
  • I'm being asked to apply (*) to the elements of coeff and [0..] one by one. Since both arguments to (*) have to be the same type and [0..] has an instance for Enum, coeff must also have an instance for Enum.
  • Error! The type signature of diff only mentions that coeff has an instance for Num, but I've already determined that it must at least have an instance for Enum too.
Michael Steele
This explains why another function did not throw up this error. That function involved a simple `zip`: `(a,b) <- zip coeff [0..]` returns tuples, which can have mixed data types.
Zaid