有一件事,让我早卡住时学习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
Run Code Online (Sandbox Code Playgroud)
VS
Prelude> :t foldl (+) 0 [1,2,3]
foldl (+) 0 [1,2,3] :: Num b => b
Run Code Online (Sandbox Code Playgroud)
Haskell/GHC如何得出这种类型foldl + 0 [1,2,3]?我怎么能理解为什么它扩展到这种巨型?
因为+是一个中缀运算符而没有括号覆盖的东西,
foldl + 0 [1,2,3]
Run Code Online (Sandbox Code Playgroud)
解析为
(foldl) + (0 [1,2,3])
Run Code Online (Sandbox Code Playgroud)
最简单的起点foldl是众所周知的类型(如果你不知道,你可以问GHCI :t foldl).
foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a
Run Code Online (Sandbox Code Playgroud)
接下来,添加的另一面.因为0作为一个函数[1,2,3]作为参数应用,所以必须有一个函数类型的Num实例,它将一些数字类型的列表作为输入,并产生输出......好吧,我们将会这样做.
0 [1,2,3] :: (Num t, Num ([t] -> s)) => s
Run Code Online (Sandbox Code Playgroud)
因为+要应用于这两个表达式,它们必须具有相同的类型.因此,我们必须统一
foldl :: Foldable f => (a -> b -> a) -> a -> f b -> a
Run Code Online (Sandbox Code Playgroud)
同
0 [1,2,3] :: (Num t, Num ([t] -> s)) => s
Run Code Online (Sandbox Code Playgroud)
最常见的方法是让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
Run Code Online (Sandbox Code Playgroud)
请记住,当然foldl必须具有完全相同的类型:
foldl :: (Foldable f,
Num t,
Num ([t] -> (a -> b -> a) -> a -> f b -> a))
=> (a -> b -> a) -> a -> f b -> a
Run Code Online (Sandbox Code Playgroud)
因为+来自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
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,模拟一些类型的重命名,正是GHC告诉你的.
但当然,这是一种相当愚蠢的类型.有可能有人会编写所有这些令人发指的Num实例,因此GHC尽职尽责地将其推断为有效类型.但实际上没有人写过这些实例,因此实际使用这个表达式会遇到很多麻烦.你应该做的就是修理你的括号.