Haskell dividing num
I'm having an is开发者_运维问答sue 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?
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.
You're overly constraining the type of avg. Use the more general version, avg :: (Fractional a) => [a] -> a
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))
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)
精彩评论