4
votes

I'm having an issue I want to learn more about, and how to avoid. I've got this code

len :: (Num r ) => [a] -> r
len [] = 0
len xs = 1 + len ( tail xs )

avg :: (Num t) => [t] -> Double
avg xs = ( sum xs ) / ( len xs )

Which renders the following error

len.hs:6:9:
    Couldn't match expected type `Double' against inferred type `t'
      `t' is a rigid type variable bound by
          the type signature for `avg' at len.hs:5:12
    In the expression: (sum xs) / (len xs)
    In the definition of `avg': avg xs = (sum xs) / (len xs)

Now, I know this error (thanks to irc.freenode.net#haskell) is a result of the division function

(/) :: (Fractional a) => a -> a -> a

However, I don't know what to do. My avg function signature should have nothing to do with the division opperators quirks (requiring Fractional typeclass). So, I'm left thinking the right way to overcome this is by casting to a type that impliments they Fractional typeclass but I have no idea how, or even if this is right? Any ideas?

4

4 Answers

7
votes

My avg function signature should have nothing to do with the division operator's quirks

Why is that? If you want to compute the average of a bunch of Integers, you'll have to divide at some point, so you'll have to convert them from Integers to the division-supporting type of your choice. A close look at the Num class (:i Num in ghci) reveals one problem with the type of avg: Num doesn't have enough methods — basically enough to add, multiply, and subtract. There's no guarantee that the number I give to avg can be converted to a Double at all.

If you enter an untyped function to compute an average, Haskell responds with the most generic type possible:

Prelude List> :type \x -> sum x / genericLength x
\x -> sum x / genericLength x :: (Fractional a) => [a] -> a

So that's the correct type of avg.

You might notice that avg [1,2,3 :: Integer] gives a type error. You can get around that by passing the argument to toRational or fromIntegral first, which use the Real and Integral instances for Integer, respectively.


Regarding the expression sum [1,2,3] / len [1,2,3]: It's true that a literal number like 1 has the type of Num a => a, which calls fromInteger on whatever type it turns out to be, but an expression like 1/2 has a more specific type of Fractional a => a, which you can see if you ask for the type of that expression instead of printing it out.

Something that might be helpful is :set -Wall in ghci, which turns on lots of warnings whenever a default type is chosen for you, giving a clue that the most generic type might no longer be correct.

5
votes

You're overly constraining the type of avg. Use the more general version, avg :: (Fractional a) => [a] -> a

1
votes

hhm the really problem is if it is an integral type you want to cast to a fractional type, but if it is an fractional type you want to leave it alone.

Try this

fromRational ((sum xs) % (leng xs))
0
votes

I've encountered this problem. This best I've managed to do is have two functions for averaging: one for integrals and one for fractionals:

avgInt :: (Integral i, Fractional f) => [i] -> f
avgInt xs = fromIntegral (sum xs) / fromIntegral (length xs)

avgFrac :: (Fractional f) => [f] -> f
avgFrac xs = sum xs / fromIntegral (length xs)