Haskell用于函数应用程序绑定的括号

时间:2016-06-30 04:51:37

标签: function haskell syntax operator-precedence

我正在学习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的情况下以不同方式解析上述内容。

2 个答案:

答案 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的想法......

  1. addToList (f x)的类型为[a] -> [a] - 即它是一个列表的函数。

  2. map的类型为(c -> d) -> [c] -> [d]。它不是一个列表,但有了额外的参数,它可以产生一个列表。

  3. 因此,当GHC看到addTolist (f x) map并且无法对其进行检查时,它会看到map只有一些参数,如下所示:

    addToList (f x) (map ...)
    
  4. 至少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

解析树完成后,每个节点都会标记其类型,并且可以进行类型检查。由于函数应用程序仅通过并置表示,因此类型检查器知道节点的左子节点是函数,右子节点是参数,根节点是结果。

类型检查器可以大致如下进行,对树进行预先遍历遍历。 (我会略微改变类型签名,以区分不相关的类型变量,直到它们统一起来。)

  1. addToList :: a -> [a] -> [a],因此它接受a类型的参数并返回类型为[a] -> [a]的函数。 a的值尚不清楚。
  2. f :: b -> c,因此它接受b类型的参数并返回类型c的值。 bc的值尚不清楚。
  3. x的类型为dd的值尚不清楚。
  4. 允许b ~ df可以应用于x,因此*1 :: c
  5. 允许a ~ caddToList应用于*1,因此*2 :: [a] -> [a]
  6. 哦,哦。 *2期望参数类型为[a],但它正在应用于map :: (e -> f) -> [e] -> [f]。类型检查器不知道如何统一列表类型和函数类型,这会产生您看到的错误。