我正在学习Haskell。我定义了以下功能(我知道我不需要addToList
,我也可以做无点符号我只是在玩语言概念的过程中):
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = addToList (f x) map f xs
where
addToList :: a -> [a] -> [a]
addToList x [] = [x]
addToList x xs = x:xs
这会产生编译错误:
with actual type `(a0 -> b0) -> [a0] -> [b0]'
Relevant bindings include
f :: a -> b (bound at PlayGround.hs:12:5)
map :: (a -> b) -> [a] -> [b] (bound at PlayGround.hs:11:1)
Probable cause: `map' is applied to too few arguments
In the second argument of `addToList', namely `map'
In the expression: addToList (f x) map f xs
如果我在地图上放置parantheses,它可以工作:
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = addToList (f x) (map f xs)
where
addToList :: a -> [a] -> [a]
addToList x [] = [x]
addToList x xs = x:xs
我理解函数应用程序比运算符绑定得更紧密(如Haskell - too few arguments中所讨论的那样),但是,我不理解编译器如何在不使用parantheses的情况下以不同方式解析上述内容。
答案 0 :(得分:9)
看到错误的简单方法是注意表达式:
addToList (f x) map f xs
将4个参数应用于addToList
,而:
addToList (f x) (map f xs)
将两个参数应用于addToList
(这是addToList
"期望")。
<强>更新强>
请注意,即使map
有两个参数,这个表达式:
addToList a map c d
被解析为:
(((addToList a) map) c) d
所以,这可以解释GHC的想法......
addToList (f x)
的类型为[a] -> [a]
- 即它是一个列表的函数。
map
的类型为(c -> d) -> [c] -> [d]
。它不是一个列表,但有了额外的参数,它可以产生一个列表。
因此,当GHC看到addTolist (f x) map
并且无法对其进行检查时,它会看到map
只有一些参数,如下所示:
addToList (f x) (map ...)
至少addToList
的第二个参数是一个列表 - 所以也许这就是问题。
答案 1 :(得分:7)
解析是在发生类型检查之前完成的一个独特步骤。表达式
addToList (f x) map f xs
对解析器的含义与s1 (s2 s3) s4 s2 s5
对你有同样的意义。它不知道名称的含义。它采用字符串的词法结构并将其转换为类似
*5
/ \
/ xs
*4
/ \
/ f
*3
/ \
/ map
*2
/ \
addToList *1
/ \
f x
解析树完成后,每个节点都会标记其类型,并且可以进行类型检查。由于函数应用程序仅通过并置表示,因此类型检查器知道节点的左子节点是函数,右子节点是参数,根节点是结果。
类型检查器可以大致如下进行,对树进行预先遍历遍历。 (我会略微改变类型签名,以区分不相关的类型变量,直到它们统一起来。)
addToList :: a -> [a] -> [a]
,因此它接受a
类型的参数并返回类型为[a] -> [a]
的函数。 a
的值尚不清楚。f :: b -> c
,因此它接受b
类型的参数并返回类型c
的值。 b
和c
的值尚不清楚。x
的类型为d
。 d
的值尚不清楚。b ~ d
,f
可以应用于x
,因此*1 :: c
a ~ c
,addToList
应用于*1
,因此*2 :: [a] -> [a]
*2
期望参数类型为[a]
,但它正在应用于map :: (e -> f) -> [e] -> [f]
。类型检查器不知道如何统一列表类型和函数类型,这会产生您看到的错误。