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

Why doesn't this haskell type signature work?

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

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

平均应该取任何数字和 return 一些数字。但是我收到错误

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

好像是说长度没有达到预期。谁能帮忙?

好的,这会起作用:

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

请注意 div :: Integral a => a -> a -> a 所以你需要 Integral a 而不是 Num a(没有除法)

而且因为 length 会 return 一个 Int 所以你需要 fromIntegral 来解决它。

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

类型

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

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)

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

中的 genericLength
import Data.List

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