1
votes

New guy at haskell here. I'm trying to get better at writing type signatures and this simple one doesn't want to work. I'm wondering why:

average :: Num a => [a] -> a
average ns = sum ns `div` length ns

Average should take any number and return some number. However I get the error

test.hs 11:27:
    Couldn't match expected type 'a' with actual type 'Int'
       'a' is a rigid type variable bound by
       the type signature for average :: Num a => [a] -> a
        at test.hs:10:12
    Relevant bindings include
        ns:: [a] (bound at test.hs:11:9)
        average :: [a] -> a (bound at test.hs:11:1)
    In the second argument of 'div' namely 'length ns'
    In the expression: sum ns `div ` length ns

It seems to be saying length isn't getting what it's expecting. Can anyone help?

3
it's because length ns is an Int but div works for Integral a => a -> a -> a - try average ns = sum ns div` (fromIntegral $ length ns)` - it should workRandom Dev
but on small thing: div needs Integral anyway !Random Dev
length :: [q] -> Int so average :: [Int] -> Int.AJF

3 Answers

3
votes

ok this will work:

average :: Integral a => [a] -> a
average ns = sum ns `div` (fromIntegral $ length ns)

please note that div :: Integral a => a -> a -> a so you need Integral a instead of just Num a (which has no division)

And because length will return an Int so you need the fromIntegral to work around it.

2
votes

The type of div is Integral a => a -> a -> a, which means that both of its arguments have to be of the same type. In this case, the second argument is always of type Int (the result of length), but the first argument is of type a.

In addition, you're trying to write a function that works for any kind of Num, but div only works for integegral types. If you want to support "averaging" lists of integral values (with the result also being rounded to an integer), you could do:

average :: Integral a => [a] -> a
average ns = sum ns `div` fromIntegral (length ns)

The fromIntegral will convert the Int length into whatever integral type a is (which might be Int, or something like Integer).

To avoid rounding error, you'll need to use fractional division on fractional types:

average :: Fractional a => [a] -> a
average ns = sum ns / fromIntegral (length ns)
0
votes

The other answers have well highlighted the issue with Num vs. Integral an offer to use a cast after calling length. Alternatively, you could use genericLength from Data.List:

import Data.List

average :: Integral a => [a] -> a
average ns = sum ns `div` genericLength ns