GHC如何解释`foldl + 0 [1,2,3]`(括号周围没有括号)?

时间:2017-07-11 15:19:02

标签: haskell types ghc

让我在学习Haskell时遇到困难的一件事是foldl +foldl (+)之间的区别。

Prelude> :t foldl + 0 [1,2,3]
  :: (Num t1, Num ((b -> a -> b) -> b -> t a -> b),
      Num ([t1] -> (b -> a -> b) -> b -> t a -> b), Foldable t) =>
     (b -> a -> b) -> b -> t a -> b

VS

Prelude> :t foldl (+) 0 [1,2,3]
foldl (+) 0 [1,2,3] :: Num b => b

Haskell / GHC如何得出foldl + 0 [1,2,3]的类型?我怎么能理解为什么它扩展到这种巨型?

3 个答案:

答案 0 :(得分:8)

因为+是一个中缀运算符,并且没有括号覆盖事物,

foldl + 0 [1,2,3]

解析为

(foldl) + (0 [1,2,3])

最简单的起点是foldl的类型,这是众所周知的(如果您不知道,您可以通过:t foldl询问GHCI。)

foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a

接下来,另外一面补充。因为0作为一个函数应用[1,2,3]作为参数,所以必须有一个函数类型的Num实例,它将一些数字类型的列表作为输入,并产生输出...好吧,我们会做到的。

0 [1,2,3] :: (Num t, Num ([t] -> s)) => s

由于+正在应用于这两个表达式,因此它们必须具有相同的类型。因此,我们必须统一

foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a

0 [1,2,3] :: (Num t, Num ([t] -> s)) => s

最常见的方法是让sfoldl完全相同(结合他们的约束),给我们

0 [1,2,3] :: (Foldable f,
              Num t, 
              Num ([t] -> (a -> b -> a) -> a -> f b -> a)) 
          => (a -> b -> a) -> a -> f b -> a

请记住,当然foldl必须具有完全相同的类型:

foldl :: (Foldable f, 
          Num t,
          Num ([t] -> (a -> b -> a) -> a -> f b -> a)) 
      => (a -> b -> a) -> a -> f b -> a

由于+来自Num类型类,因此它们共享的类型也必须为Num。

foldl + 0 [1,2,3] :: (Foldable f, 
                      Num t, 
                      Num ([t] -> (a -> b -> a) -> a -> f b -> a),
                      Num ((a -> b -> a) -> a -> f b -> a))
                  => (a -> b -> a) -> a -> f b -> a

正如你所看到的,模拟一些类型的重命名,就像GHC告诉你的那样。

但当然,这是一种相当愚蠢的类型。它是可能的有人会写出所有这些令人发指的Num实例,所以GHC尽职尽责地将其推断为有效类型。但实际上没有人写过这些实例,因此实际使用这个表达式会遇到很多麻烦。你应该做的就是修理你的括号。

答案 1 :(得分:0)

foldl + 0 [1,2,3]被解析为foldl0 [1,2,3]的总和,0应用于列表[1,2,3]

答案 2 :(得分:0)

在Haskell中,括号不仅用于设置表达式求值的优先级。这两个陈述实际上是完全不同的。

符号+是运算符,所以它应该像a + b = c一样工作。也就是说,左边是一个函数参数,右边是另一个函数参数。你说运算符是在 infix 表示法中使用的。

您可以通过括号将任何运算符转换为前缀表示法中的正常函数。 (+) a b = c

因此,你提供的两个表达方式完全不同。他们很自然地拥有不同类型的签名。