如何在Haskell函数中使用类型类?

时间:2018-03-30 12:51:58

标签: haskell typeclass type-declaration

我目前正在尝试通过Learn You A Haskell自学Haskell,所以为了测试自己我正在尝试编写一个带有数字列表并返回平均值的函数。

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]))

但是,当我运行此代码时,收到此错误消息:

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)

我一直在修改签名,我能让它工作的唯一方法就是将类型变成实际类型,例如: avg :: [Int] -> Int。我非常感谢任何建议。

1 个答案:

答案 0 :(得分:2)

问题出在这里:

avg l = ((foldr (+) 0 l) `div` (listlen l))

div定义在Integral s上,而不只是Num s。此外,div对结果进行了落实。正常除法为(/),其定义为Fractional s。

此处(更直接)来自:

listlen :: [a] -> Int
listlen [] = 0
listlen (x:xs) = 1 + listlen xs

listlen会返回Int,这会使div的类型为Int -> Int -> Int

这应该有效:

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

或者,使用Prelude中的一些函数:

avg :: (Fractional a) => [a] -> a
avg [] = error "Cannot average list of length 0"
avg l = sum l / fromIntegral (length l)