为什么这个haskell型签名不起作用?

时间:2015-05-25 19:46:22

标签: haskell types type-signature

哈斯克尔的新人在这里。我正在尝试更好地编写类型签名,这个简单的签名不想工作。我想知道为什么:

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

平均值应该取任何数字并返回一些数字。但是我收到了错误

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

似乎说长度没有达到预期的水平。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:3)

好的,这将有效:

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

请注意div :: Integral a => a -> a -> a所以您需要Integral a而不仅仅是Num a(没有分割)

因为length会返回Int,所以您需要fromIntegral来解决它。

答案 1 :(得分:2)

div的类型是Integral a => a -> a -> a,这意味着它的两个参数必须属于同一类型。在这种情况下,第二个参数始终为Int类型(length的结果),但第一个参数的类型为a

此外,您正在尝试编写适用于任何类型Num的函数,但div仅适用于整数类型。如果你想支持"平均"积分值列表(结果也四舍五入为整数),你可以这样做:

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

fromIntegral会将Int长度转换为任何整数类型a(可能是Int,或类似Integer)。

为避免舍入错误,您需要对小数类型使用小数除法:

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

答案 2 :(得分:0)

其他答案很好地突出了NumIntegral在调用length后使用强制转换的提议的问题。或者,您可以使用genericLength中的Data.List

import Data.List

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