[Haskell-cafe] A systematic method for deriving a defintion of foldl using foldr?

wren ng thornton wren at freegeek.org
Sun Mar 15 01:50:34 EDT 2009


R J wrote:
> 2.  I believe that the reverse implementation--namely, implementing
> foldr in terms of foldl--is impossible.  What's the proof of that?

As others have said, foldr in terms of foldl is impossible when infinite 
lists are taken into account. For finite lists it's easy though:

(\f z xs -> foldl (\yz y -> yz . f y) id xs z)


> 3.  Any advice on how, aside from tons of practice, to develop the
> intuition for rapidly seeing solutions to questions like these would
> be much appreciated.  The difficulty a newbie faces in answering
> seemingly simple questions like these is quite discouraging.

The solutions to this problem, while "seemingly simple", is not so 
simple that a newbie should get discouraged, IMO.

The essential trick here is to come up with the idea of using the fold 
to build a function, which in turn actually does what you want--- rather 
than trying to do anything "useful" with the fold itself. This idea 
comes in two parts: implementation indirection (let another function do 
the real work) and continuation-passing (to build the other function). 
Both of those tricks are ones we use all the time, but the foldr/foldl 
problem weds them together in a particularly perverse (though 
deliciously elegant) manner.


In order to develop an intuition for the interplay of CPS and recursion, 
consider this exercise: You have a type for binary trees and you have 
this function for getting the size of a tree:

     > size (Leaf _)     = 1
     > size (Branch l r) = size l + size r

Unfortunately you have very deep trees and so this function gives 
stack-overflow errors. Rewrite it to be tail recursive.

That one's not too hard because the order of recursion doesn't matter 
(addition is associative and commutative). Now, you have a similar 
function and you're worried about the fact that repeated list 
concatenation can take O(n^2) time. Rewrite this one so it's tail 
recursive--- making sure that the leaves still come out in the right 
order. If you're familiar with the difference list trick[1] then also 
rewrite it so you only take O(n) time building the list.

     > toList (Leaf x)     = [x]
     > toList (Branch l r) = toList l ++ toList r


[1] See http://hackage.haskell.org/cgi-bin/hackage-scripts/package/dlist 
or look at the ShowS type used in the Prelude for the shows function.

-- 
Live well,
~wren


More information about the Haskell-Cafe mailing list