在学习 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
对比
Prelude> :t foldl (+) 0 [1,2,3]
foldl (+) 0 [1,2,3] :: Num b => b
Haskell/GHC 如何导出
foldl + 0 [1,2,3]
的类型?我怎么能理解为什么它会扩展到这种巨大的类型? 最佳答案
因为 +
是一个中缀运算符并且没有括号覆盖事物,
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
最通用的方法是让
s
与 foldl
的类型完全相同(结合它们的约束),给我们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 尽职尽责地将其推断为有效类型。但是实际上没有人写过这些实例,所以在实际使用这个表达式时会遇到很多麻烦。你真正应该做的是修正你的括号。
关于haskell - GHC 如何解释 `foldl + 0 [1,2,3]`(+ 周围没有括号)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45038549/