这是我在Haskell的第一次探索,所以请原谅我是否应该是显而易见的。
我整个下午一直在玩Haskell,使用我自己的列表类型(典型的缺点)筛选教程99 questions on HaskellWiki。我继续添加了“明显”的功能,并且我尽可能地使它们尽可能简洁(尽可能使用无点符号)
第12个问题是关于解码游程编码列表,即:
> decode [Multiple 5 'a', Single 'b', Multiple 2 'c']
"aaaaabcc"
我考虑使用map
解码每个元素,然后concat
结果(感谢Google就此了),最后还记得我在阅读材料中看到concatMap
之类的东西,GHCi很快确认:
> :t map
map :: (a -> b) -> [a] -> [b]
> :t concat
concat :: [[a]] -> [a]
> :t concatMap
concatMap :: (a -> [b]) -> [a] -> [b]
重新实现concatMap
看起来很明显:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap = concat . map
除了GHCi非常抱怨:
List.hs:110:15:
Couldn't match expected type `[a] -> [b]'
with actual type `[a0]'
Expected type: [[a0]] -> [a] -> [b]
Actual type: [[a0]] -> [[a0]]
In the first argument of `(.)', namely `concat'
In the expression: concat . map
我无法理解,所以我在网上查找,Prelude中引用的定义实际上是:
concatMap f = concat . map f
而且我不太明白为什么这个f是必要的,因为它的类型显然是签名所指定的a -> [b]
......
那么为什么f
必须在这里?
答案 0 :(得分:15)
从标准定义开始,
concat . map f
≡ concat . (map f)
≡ \x -> concat ((map f) x)
您的第一个定义为您提供:
(concat . map) f
≡ (\x -> concat (map x)) f
≡ concat (map f)
由于(map f) :: [a] -> [b]
而没有输入格式,而concat
需要[[a]]
列表。
请注意,问题中显示的特定错误消息并未描述上述类型检查失败。给定的消息来自于将返回类型concatMap
声明为[a] -> [b]
,这与[a0]
无法协调,返回类型为concat
。如果您不使用类型签名,则响应为:
Couldn't match type ‘[a1] -> [b]’ with ‘[[a]]’ Expected type: (a1 -> b) -> [[a]] Actual type: (a1 -> b) -> [a1] -> [b] Relevant bindings include concatMap :: (a1 -> b) -> [a] (bound at :49:5) Probable cause: ‘map’ is applied to too few arguments In the second argument of ‘(.)’, namely ‘map’ In the expression: concat . map
此处,在将map
的返回类型与参数类型concat
进行协调时会发生类型错误。事实证明,这种情况对于调试更有用,因为它包含一个提示,为什么类型检查失败。