为什么我在自定义类型上的地图实现不正确?

时间:2016-06-12 12:38:36

标签: haskell

我跟随https://github.com/NICTA/course

进行了List练习

以下代码段从https://github.com/NICTA/course/blob/master/src/Course/List.hs

的一部分复制而来
data List t =
  Nil
  | t :. List t
  deriving (Eq, Ord)

map ::
  (a -> b)
  -> List a
  -> List b
map f a = filter (\listElement -> listElement /= Nil) a

以上给出了以下错误:
无法将预期类型'b'与实际类型匹配'列表t0''b'是由map ::(a - > b)的类型签名绑定的刚性类型变量 - >列出一个 - >列表b

我试图实现以下目标:

>>> map (+10) (1 :. 2 :. 3 :. Nil)
[11,12,13]

2 个答案:

答案 0 :(得分:5)

首先,解释错误消息:您的定义中不能使用filter,因为

 filter :: (a -> Bool) -> [a] -> [a]

与常规Prelude列表有关,而不是List s - 即[a]而不是List a。出现错误消息是因为filter期望

中的a
 map f a = filter (\listElement -> listElement /= Nil) a

是一个列表,但你提供的签名声明aList的东西。类似地,filter返回某事物的Prelude列表,但签名要求它返回List某事物。

map的{​​{1}}的自然实现会区分您在类型声明中提供的List的情况,即它会“模式匹配”:

List

请注意,您编写的程序完全有效,只是与您提供的签名冲突:

mapList ::
    (a -> b)
    -> List a
    -> List b
mapList f Nil = Nil
mapList f (t :. ls) = f t :. mapList f ls

ghci> let mapX f a = filter (\listElement -> listElement /= Nil) a ghci> :t mapX mapX :: Eq a => t -> [List a] -> [List a] 约束是必需的,因为你预先假定Eq被测试是否相等,因此它们的元素可以被测试。我没有使用List,所以它最终只是一个'可能是任何东西'参数,在这里f

当然,如果您拥有t filterList的{​​{1}},那么它也会出现问题

List

此函数的作用是从列表列表中删除空元素,例如ghci> let filterList pred Nil = Nil; filterList pred (a :. as) = if pred a then a :. filterList pred as else filterList pred as ghci> :t filterList filterList :: (t -> Bool) -> List t -> List t ghci> let mapY f a = filterList (\listElement -> listElement /= Nil) a ghci> :t mapY mapY :: Eq a => t -> List (List a) -> List (List a) 。同样,您定义的实际功能(没有签名)从列表的 Prelude列表中删除了Prelude.filter (not . Prelude.null)列表。

答案 1 :(得分:1)

filter (\listElement -> listElement /= Nil) a

这是类型错误的来源。如果filter的实施遵循合理的路径,则listElement应该是a的元素,也就是说,因为a的类型为List a,所以它是类型a。您将不平等与Nil类型List a进行比较。