Each foldl is a foldr .
Recall the definitions.
foldr :: (a -> s -> s) -> s -> [a] -> s foldr fs [] = s foldr fs (a : as) = fa (foldr fs as)
This is the standard release of a one-step iterator for lists. I used to make my students beat tables and say, "What are you doing with an empty list? What are you doing with a : as "? And how do you find out that s and f , respectively.
If you think about what happens, you see that foldr efficiently computes a large composition of the functions fa , then applies this composition to s .
foldr fs [1, 2, 3] = f 1 . f 2 . f 3 . id $ s
Now let's see foldl
foldl :: (t -> a -> t) -> t -> [a] -> t foldl gt [] = t foldl gt (a : as) = foldl g (gta) as
This is also a one-step iteration through the list, but with a battery that changes as we go. Let move it last so that everything to the left of the list argument remains the same.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t flip (foldl g) [] t = t flip (foldl g) (a : as) t = flip (foldl g) as (gta)
Now we can see the one-step iteration if we move = one place to the left.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t flip (foldl g) [] = \ t -> t flip (foldl g) (a : as) = \ t -> flip (foldl g) as (gta)
In each case, we calculate what we will do if we knew a battery abstracted with \ t -> . For [] we return t . For a : as we treat the tail with gta as a battery.
But now we can convert flip (foldl g) to foldr . Issue a recursive call.
flip . foldl :: (t -> a -> t) -> [a] -> t -> t flip (foldl g) [] = \ t -> t flip (foldl g) (a : as) = \ t -> s (gta) where s = flip (foldl g) as
And now we can turn it into foldr , where type s is created using t -> t .
flip . foldl :: (t -> a -> t) -> [a] -> t -> t flip (foldl g) = foldr (\ as -> \ t -> s (gta)) (\ t -> t)
So, s says, "what as will do with the battery," and we return \ t -> s (gta) that "what a : as does with the battery." Lean back.
foldl :: (t -> a -> t) -> t -> [a] -> t foldl g = flip (foldr (\ as -> \ t -> s (gta)) (\ t -> t))
Eta extensions.
foldl :: (t -> a -> t) -> t -> [a] -> t foldl gt as = flip (foldr (\ as -> \ t -> s (gta)) (\ t -> t)) t as
Reduce flip .
foldl :: (t -> a -> t) -> t -> [a] -> t foldl gt as = foldr (\ as -> \ t -> s (gta)) (\ t -> t) as t
So, we calculate βwhat would we do if we knew the batteryβ, and then we will feed it with the original battery.
This is moderately instructive in golf, which is a bit. We can get rid of \ t -> .
foldl :: (t -> a -> t) -> t -> [a] -> t foldl gt as = foldr (\ as -> s . (`g` a)) id as t
Now let me cancel this composition using >>> from Control.Arrow .
foldl :: (t -> a -> t) -> t -> [a] -> t foldl gt as = foldr (\ as -> (`g` a) >>> s) id as t
That is, foldl computes a large inverse composition. So, for example, given [1,2,3] , we get
foldr (\ as -> (`g` a) >>> s) id [1,2,3] t = ((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t
where the "pipeline" feeds its argument to the left, so we get
((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t = ((`g` 2) >>> (`g` 3) >>> id) (gt 1) = ((`g` 3) >>> id) (g (gt 1) 2) = id (g (g (gt 1) 2) 3) = g (g (gt 1) 2) 3
and if you take g = flip (:) and t = [] , you get
flip (:) (flip (:) (flip (:) [] 1) 2) 3 = flip (:) (flip (:) (1 : []) 2) 3 = flip (:) (2 : 1 : []) 3 = 3 : 2 : 1 : [] = [3, 2, 1]
I.e
reverse as = foldr (\ as -> (a :) >>> s) id as []
by instantiating the general transform foldl to foldr .
Only for matechists. Make cabal install newtype and import Data.Monoid , Data.Foldable and Control.Newtype . Add a tragically missing instance:
instance Newtype (Dual o) o where pack = Dual unpack = getDual
Note that, on the one hand, we can implement foldMap on foldr
foldMap :: Monoid x => (a -> x) -> [a] -> x foldMap f = foldr (mappend . f) mempty
but vice versa
foldr :: (a -> b -> b) -> b -> [a] -> b foldr f = flip (ala' Endo foldMap f)
so the foldr accumulates in the monoid of composing endofunctions, but now, to get the foldl , we will tell foldMap to work in the monoid Dual .
foldl :: (b -> a -> b) -> b -> [a] -> b foldl g = flip (ala' Endo (ala' Dual foldMap) (flip g))
What is mappend for Dual (Endo b) ? Modulo wrap is exactly the reverse composition, >>> .