I'm currently trying to teach myself Haskell with Learn You A Haskell, and so to test myself I'm trying to write a function that takes a list of numbers and returns the average value.
listlen :: [a] -> Int
listlen [] = 0
listlen (x:xs) = 1 + listlen xs
avg :: (Num a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = ((foldr (+) 0 l) `div` (listlen l))
main = putStrLn (show (avg [1,2,3,4,5]))
However, when I run this code, I get this error message:
test.hs:7:10: error:
• Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
the type signature for:
avg :: forall a. Num a => [Int] -> a
at test.hs:5:8
• In the expression: ((foldr (+) 0 l) `div` (listlen l))
In an equation for ‘avg’:
avg l = ((foldr (+) 0 l) `div` (listlen l))
• Relevant bindings include
avg :: [Int] -> a (bound at test.hs:6:1)
I've been tinkering with the signature and the only way I can get it to work is to make the types into actual types, e.g. avg :: [Int] -> Int
. I would greatly appreciate any advice.
The problem is from here:
avg l = ((foldr (+) 0 l) `div` (listlen l))
div
is defined over Integral
s, not just Num
s. Also, div
floors the result. Normal division is (/)
, which is defined over Fractional
s.
Also (more directly) from here:
listlen :: [a] -> Int
listlen [] = 0
listlen (x:xs) = 1 + listlen xs
listlen
returns Int
, which makes div
have type of Int -> Int -> Int
.
This should work:
listlen :: (Num n) => [a] -> n
listlen [] = 0
listlen (x:xs) = 1 + listlen xs
avg :: (Fractional a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = foldr (+) 0 l / listlen l
Or, with some functions from Prelude
:
avg :: (Fractional a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = sum l / fromIntegral (length l)