类型声明有什么问题吗?

时间:2017-11-30 10:23:32

标签: haskell higher-order-functions

我正在阅读“Haskell编程”一书。一个练习要求我使用高阶函数定义map f。我选择定义map (+1),如下所示:

unfold p h t x | p x       = []
               | otherwise = h x : unfold p h t (t x)

-- equivalent to `map (+1)`
mapinc = unfold (==[]) ((+1).head) tail

(直接来自练习题)如果谓词unfold p h t为参数值为true,则函数p生成空列表,否则通过应用函数{{}生成非空列表{1}}给这个值赋予头部,函数h生成另一个参数,以相同的方式递归处理以生成列表的尾部。

我已经检查了t的实现,看起来很好:

mapinc

但是,在我添加了类型声明之后:

*Main> mapinc [1,2,3]
[2,3,4]

然后在WinGHCi中重新加载脚本,它会出现以下错误:

mapinc :: Num a => [a] -> [a]
mapinc = unfold (==[]) ((+1).head) tail

任何线索为何会发生?

1 个答案:

答案 0 :(得分:5)

您的签名太宽。您编写的谓词是== []。 Haskell只能检查两个列表是否相等,是否也可以检查列表的元素。在源代码中我们看到类似的东西:

instance Eq a => Eq [a] where
    ...

是的,在这里我们永远不会检查两个项目的相等性,因为我们检查空列表,但编译器当然不知道:它只是看到为了检查两个列表是否相等,我们需要能够检查元素是否相等。

Num类型类暗示该类型也是Eq类型。我们可以在这里做两件事:

  1. Eq类型约束添加到签名:

    mapinc :: (Eq a, Num a) => [a] -> [a]
    mapinc = unfold (==[]) ((+1).head) tail
  2. 更优雅:不要依赖于我们需要能够比较元素的事实,而是使用null :: [a] -> Bool代替(检查列表是否为空的函数):

    mapinc :: Num a => [a] -> [a]
    mapinc = unfold null ((+1).head) tail