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!
map is a function that takes two parameters: a function and a list of elements. The type signature of map is (a -> b) -> [a] -> [b] . The (a -> b) part is the function you pass to map , we will call it f . f takes one value and returns another that may be of a different type.
Difference Between foldl and foldr The difference is that foldl is tail-recursive, whereas foldr is not. With foldr and non-optimized patterns, proc is applied to the current value and the result of recursing on the rest of the list. That is, evaluation cannot complete until the entire list has been traversed.
To recap, with foldr , the purpose of the function argument is to take the first element of the list and the results of having folded the rest of the list, and return the new value. With foldl , the function argument takes a default value, the first element of the list, and returns a new default value.
The foldr function, pronounced `fold right', is also usually implemented as a curried function and has an even more sophisticated type than map. Its type is (('a * 'b) -> 'b) -> ('b -> (('a list) -> 'b)).
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
:)
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 Functor
s 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With