8

After I defined map using foldr a question came to my mind:

If it is possible to define map using foldr, what about the opposite?

From my point of view it is not possible, but I can't find a proper explanation. Thanks for the help!

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Josh
  • 83
  • 5
  • 4
    `map` outputs a list of the same length it gets. `foldr` can output a list of any length, or something that is not a list at all. – n. m. could be an AI May 24 '14 at 22:25
  • Somewhat relevant: http://web.jaguarpaw.co.uk/~tom/blog/posts/2012-11-04-what-is-foldr-made-of.html – Tom Ellis May 24 '14 at 22:52
  • 2
    You have to be a little more specific. Note that it's possible to define `foldr` without using anything but pattern matching, so you certainly can define it using pattern matching *and* `map`. – Piotr Miś May 24 '14 at 22:55

3 Answers3

15

Let's start with some type signatures.

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

We can simulate map using fold because fold is a universal operator (here's a more mathematical yet quite friendly paper on this property).

I'm sure that there's some creative way of using map to simulate foldr. That can certainly be a fun exercise. But I don't think there's a straight-forward, not "crazy pointfree" solution, and in order to explain it let's forget about foldr for a moment and concentrate on a much simpler accumulation function:

sum :: [Int] -> Int

sum == foldr (+) 0, which means foldr implements sum. If we can implement foldr with map we can definitely implement sum with map. Can we do it?

I think sum's signature is a crashing blow - sum returns an Int, and map always returns a list of something. So maybe map can do the heavy-lifting, but we'll still need another function of type [a] -> a in order to get the final result. In our case, we'll need a function of type [Int] -> Int. Which is quite unfortunate, because that's exactly what we were trying to avoid in the first place.

So I guess the answer is: you can implement foldr using map - but it'll probably require using foldr :)

Benesh
  • 3,398
  • 1
  • 18
  • 38
  • While reading this I couldn't stop thinking "Curry-Howard strikes again" ;-) – chi May 25 '14 at 19:40
  • if `zip` is allowed, [`scanl` *can* be implemented with `map`](http://stackoverflow.com/a/11951590/849891), and with the addition of `last` - also `foldl`, and `sum` in particular. lazy `foldr` on infinite lists might be problem. – Will Ness Aug 26 '15 at 22:48
5

The simplest way to look at it is to see that map preserves the spine of the list. If you look at the more general fmap (which is map, but not just for lists but for Functors in general), it's even a law that

fmap id = id

There are many ways to "cheat," but in the most direct interpretation of your question, folds are simply more general than maps. There is a nice trick that's used in Edward Kmett's Lens library quite a lot. Consider the Const monad, which is defined as follows:

newtype Const a b = Const { runConst :: a }

instance Functor (Const a) where fmap _ (Const a) = Const a
instance (Monoid a) => Monad (Const a) where
    return _ = Const mempty
    Const a >>= Const b = Const (a <> b)

Now you can formulate a fold in terms of the monadic map operation mapM, as long as the result type is monoidal:

fold :: Monoid m => [m] -> m
fold = runConst . mapM Const
holzensp
  • 51
  • 1
2

If you make some sort of cheating helper function:

f [x] a = x a
f (x:xs) a = f xs (x a)

foldr g i xs = f (map g $ reverse xs) i
  • this `f` is `f = foldl (>>>) id = foldr (.) id . reverse` so it's not even cheating, it's just folding again, `foldr (.) id (reverse $ map g $ reverse xs) i = foldr ((.).g) id xs i = foldr g i xs`. – Will Ness Aug 26 '15 at 13:32