foldr in terms of map

Glynn Clements glynn.clements@virgin.net
Tue, 1 Jul 2003 11:24:15 +0100


Hal Daume wrote:

> > map f = foldr ((:) . f) []
> 
> as I understand it, this is essentially because foldr encapsulates all
> primitive recursive functions and since map is primitive recursive, we
> can implement it in terms of a fold.
> 
> one thing that is interesting to note is that if we are also given
> references, a function to sequence monadic actions ('sequence_') and
> reverse, we can write foldr in terms of map

Yeah, but given that sequence_ is essentially the direct monadic
translation of fold:

	sequence_        :: Monad m => [m a] -> m ()
	sequence_         = foldr (>>) (return ())

that might be considered cheating (i.e. we can implement fold using
only map and fold, although the fold can be "disguised").

> now, when we're in the IO monad, the difference between the real foldr
> and the simulated one is that the simulated one cannot deal with
> infinite inputs.  for instance:
> 
> > head $ foldr (:) [] [1..]
> 
> should return 1, which it does for the real version.  the simulated one
> hangs.
> 
> one might like to attribute this to the fact that IO is a strict monad,

Yep.

> but that doesn't seem to be all -- we would also need to be able to read
> and write references lazily in order to get this to work completely.

Which is basically the same thing as "IO is strict".

> Q1: is this correct?  is there a way to "fix" the above definition or to
> use a different monad to get laziness?  i think not, but can't prove it.

However the monad is defined, sequence_ has to process the entire list
before anything can be determined about the result. The entire result
of (>>) depends upon both arguments, whereas you can deduce the head
of the result of (:) based solely upon the first argument.

> now, if we just limit ourselves to finite inputs, things look a lot
> better.  however, this brings up really the main question i have.
> 
> foldr (in its original form) is in some sense quintessentially(sp?) PR
> on lists, whereas map is not.  however, it seems that the combination
> map+rsequence_+references is enough to give you full PR power.

As mentioned above, sequence_ etc are essentially the embodiment of
primitive recursion.

> what more can we say about this?  clearly references play the largest
> role here,

I disagree; sequencing is the key factor. The core issue is that each
recursive call receives a parameter which depends upon all previous
steps, unlike map, where each step is independent (for a monadic
implementation, this actually occurs in the "run" operation, e.g. 
runST).

An implementation using a simple state-transformer monad (s -> (a, s))
wouldn't look significantly different to one using IORef/STRef.

> but it also seems impossible to remove this dependence on the
> sequencing operation.

Yep.

-- 
Glynn Clements <glynn.clements@virgin.net>