Alfredo Di Napoli
Alfredo Di Napoli

Reputation: 2301

Haskell's type associative chain is puzzling

I'm playing with some Haskell code. I've defined two functions:

count :: [a] -> Int
count []     = 0
count (x:xs) = 1 + (count xs)

-- 03. Write a function that computes the mean of a list, i.e., the sum of all 
-- elements in the list divided by its length. (You may need to use the 
-- fromIntegralfunction to convert the length of the list from an integer 
-- into a floating-point number.)

-- I've guessed this type definition, but it's incorrect:
-- listMean :: [a] -> Double
-- The following is the same inferred by hgci
listMean :: (Fractional a) => [a] -> a
listMean lst = (foldl1 (+) lst)/(fromIntegral (count lst))

Why [a] -> Double is not correct? It seems that I give lst that is a generic list of some type a and listMean returns a Double. What am I doing wrong?

Thanks, Alfredo

Upvotes: 2

Views: 309

Answers (1)

Jeremy W. Sherman
Jeremy W. Sherman

Reputation: 36143

First of all, listMean :: [a] -> Double says that listMean is a function taking a list of any type a to a single Double value.

But you rely on being able to apply (+) to the elements of the list (foldl1 (+)), which requires that the type a be an instance of Num, which means you have at least:

listMean :: (Num a) => [a] -> b

You also apply (/) to the value of type a that results from the foldl1 operation. To do so, a must not only be an instance of Num, but of Fractional. Applying this requirement to the type a gives the type signature:

listMean :: (Fractional a) => [a] -> b

Now, what of b? Well, the signature for (/) is (/) :: (Fractional a) => a -> a -> a. So, the result of listMean must also be an instance of Fractional. Further, it must be the same instance of Fractional as that contained in the list: type b is thus, in fact, type a, and listMean's most general signature is:

listMean :: (Fractional a) => [a] -> a

This is exactly what the compiler inferred. If you want to specialize this for Double, you would have to replace both occurrences of a with Double:

listMean :: [Double] -> Double

This is because you have no operator in there that will coerce any instance of Fractional into a Double, so both input and output to (/) must be of type Double.

Upvotes: 20

Related Questions