foldl编译但不折叠

时间:2015-12-07 19:02:16

标签: haskell functional-programming

作为一个小练习,我试图重新编码。这段代码似乎有效(除了无限列表):

allComp f acc x = acc && f x

all :: (a -> Bool) -> [a] -> Bool
all f xs = foldl (allComp f) True xs

但如果我保留相同的签名,那么这个就不会编译:

allComp f acc x = acc && f x

all :: (a -> Bool) -> [a] -> Bool
all f xs = foldr (allComp f) True xs

编译错误是:

Couldn't match type `a' with `Bool'
  `a' is a rigid type variable bound by
      the type signature for all :: (a -> Bool) -> [a] -> Bool
      at Main.hs:8:8
Expected type: Bool -> Bool
  Actual type: a -> Bool
In the first argument of `allComp', namely `f'
In the first argument of `foldr', namely `(allComp f)'
In the expression: foldr (allComp f) True xs

任何人都可以更清楚地解释为什么第一个版本编译而不是第二版?

1 个答案:

答案 0 :(得分:4)

如果您在Hoogle上进行搜索,则会看到foldlfoldr的签名不同:

foldl :: (a -> b -> a) -> a -> [b] -> a
foldr :: (a -> b -> b) -> b -> [a] -> b

现在让我们让签名更加语义上

foldl :: (a -> b -> a) -> a -> [b] -> a
foldr :: (b -> a -> a) -> a -> [b] -> a

您会看到第一个参数(函数)中类型的顺序不同。这是因为foldl看起来像:

foldl f z [x1,x2,...,xn] = f (f (f (... (f z x1) x2) x3)...) xn

foldr看起来像:

foldr f z [x1,x2,...,xn] = f z (f x1 (f x2 (... (f xn-1 xn) ...)))

由于(&&)是关联的,因此无关紧要。您只需使用flip :: (a -> b -> c) -> b -> a -> c

进行更改即可
all f xs = foldr (flip (allComp f)) True xs