阅读"真实世界Haskell",在第95页,作者提供了一个例子:
myFoldl f z xs = foldr step id xs z
where step x g a = g (f a x)
我的问题是:为什么要编译这个代码?foldr
只接受三个参数,但在这里,它传递了四个参数:step
、id
、xs
和z
。
例如,这不起作用(因为sum需要一个):
sum filter odd [1,2,3]
相反,我必须写:
sum $ filter odd [1,2,3]
以下是foldr
:的类型
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
我们能弄清楚它是如何变成四元函数的吗?让我们试一试吧!
我们给它
id :: d -> d
作为第二个参数(b
),所以让我们把它代入类型:(a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> (d -> d)
在Haskell中,
a -> a -> a
与a -> (a -> a)
相同,后者给出了(去掉最后一组括号):(a -> (d -> d) -> (d -> d)) -> (d -> d) -> [a] -> d -> d
用
e
代替(a -> (d -> d) -> (d -> d))
,用f
代替(d -> d
),使其更易于阅读:e -> f -> [a] -> d -> d
所以我们可以清楚地看到,我们已经构造了一个四参数函数!我的头很疼。
下面是一个从n-arg函数创建n+1参数函数的简单示例:
Prelude> :t id
id :: a -> a
id
是一个自变量的函数。
Prelude> id id id id id 5
5
但我只给了它5个args!
这是因为foldr
的多态性:
foldr :: (a -> b -> b) -> b -> [a] -> b
这里,我们已经将b
实例化为一个函数类型,我们称之为c -> c
,因此foldr
的类型专门用于(例如)
foldr :: (a -> (c -> c) -> (c -> c)) -> (c -> c) -> [a] -> c -> c
foldr
只接受3个参数
错了。Haskell中的所有函数都只接受一个参数,并产生一个结果。
foldr :: (a -> b -> b) -> b -> [a] -> b
请参阅,foldr
采用一个参数(a -> b -> b)
,并产生1个结果:b -> [a] -> b
。当你看到这个:
foldr step id xs z
记住,这只是的简写
((((foldr step) id) xs) z)
这就解释了为什么这是无稽之谈:
sum filter odd [1,2,3]
(((sum filter) odd) [1,2,3])
sum :: Num a => [a] -> a
将列表作为其输入,但您给了它一个函数。