我正在阅读“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
任何线索为何会发生?
答案 0 :(得分:5)
您的签名太宽。您编写的谓词是== []
。 Haskell只能检查两个列表是否相等,是否也可以检查列表的元素。在源代码中我们看到类似的东西:
instance Eq a => Eq [a] where
...
是的,在这里我们永远不会检查两个项目的相等性,因为我们检查空列表,但编译器当然不知道:它只是看到为了检查两个列表是否相等,我们需要能够检查元素是否相等。
Num
类型类不暗示该类型也是Eq
类型。我们可以在这里做两件事:
将Eq
类型约束添加到签名:
mapinc :: (Eq a, Num a) => [a] -> [a]
mapinc = unfold (==[]) ((+1).head) tail
更优雅:不要依赖于我们需要能够比较元素的事实,而是使用null :: [a] -> Bool
代替(检查列表是否为空的函数):
mapinc :: Num a => [a] -> [a]
mapinc = unfold null ((+1).head) tail