When you pass (+)
in to the first argument of foldr
you implicitly declare that a
is the same as b
. This gets confusing since we tend to reuse names, but if I write it all together using the same namespace for the type variables
(+) :: Num i => i -> i -> i
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr (+) :: Num i => i -> [i] -> i
where the third line implies that i ~ a
and i ~ b
thus, by transitivity, a ~ b
. Also, it might be more clear here to see that the first remaining parameter in foldr (+)
is an "initial" value for the fold and the [i]
bit is the list that we're compressing, folding, reducing.
It might be even more clear to call foldr (+) 0
by it's more common name, sum :: Num a => [a] -> a
. We also have foldr (*) 1
as product :: Num a => a -> [a]
.
So yes, your description of how the accumulator function is behaving in foldr (+)
is exactly correct, but more specific than the function is in general. For instance, we can use foldr
to build a Map
.
foldr (\(k, v) m -> Map.insert m k v) Map.empty :: Ord k => [(k, v)] -> Map k v
In this case the accumulator function takes our association list and keeps inserting the values into our accumulat*ing* Map
, which begins empty. To be utterly thorough here, let me write out the types all together again
(\(k, v) -> m -> Map.insert m k v) :: Ord k => (k, v) -> Map k v -> Map k v
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr (\(k, v) -> m -> Map.insert m k v) :: Ord k => Map k v -> [(k, v)] -> Map k v
where we've forced a ~ (k, v)
and b ~ Map k v
.
As a final view on the matter, here's the definition of foldr with some suggestive variable names
foldr _ b [] = b
foldr (<>) b (a:as) = a <> foldr f b as
So you can see how (<>) :: a -> b -> b
combines a
and b
types. We can "run" this definition explicitly to see how it builds up the computation.
foldr (+) 0 [1,2,3]
1 + (foldr (+) 0 [2,3])
1 + (2 + (foldr (+) 0 [3]))
1 + (2 + (3 + (foldr (+) 0 [])))
1 + (2 + (3 + 0))
1 + (2 + 3)
1 + 5
6
Which may be even more clear when we use a non-symmetric operation like the Map
example above. Below I'm using {{ k -> v, k -> v }}
to represent the Map
since it isn't printable directly.
-- inserts a single (k,v) pair into a Map
ins :: Ord k => (k, v) -> Map k v -> Map k v
ins (k, v) m = Map.insert m k v
foldr ins Map.empty [('a', 1), ('b', 2)]
ins ('a', 1) (foldr ins Map.empty [('b', 2)])
ins ('a', 1) (ins ('b', 2) (foldr ins Map.empty []))
ins ('a', 1) (ins ('b', 2) Map.empty)
ins ('a', 1) (ins ('b', 2) {{ }})
ins ('a', 1) {{ 'b' -> 2 }}
{{ 'b' -> 2, 'a' -> 1 }}