Maps are Folds in Functional Programming. Here’s how:

Anirudh Eka
3 min readApr 8, 2018

--

One thing I really enjoy in programming (and in life) is when I discover a deeper truth that underpins what seem like unrelated things. There’s something spiritual about it.

I wanted to share such a discovery that I made a couple of years ago when I was new to Haskell and wanted to kill time in the Yangon International Airport.

The problem I was working on required me to filter a list into a sublist based on a function that goes through each item of a list and says if it belongs or doesn’t, by returning True or False. The method signature should look like this:

(a -> Bool) -> [a] -> [a]

(Prelude, the Haskell standard library, already has a function with this signature called filter to do this, but I wanted to see if I could implement it without it.)

To build this function the first thing I asked myself was "should I use a map or a fold?"

What is a map?

At a high level, map takes a list and return a list of the same length with each item transformed by a function:

-- given f and [a, b, c] -map-> returns [f a, f b, f c]
-- given +1 and [1, 2, 3] -map-> returns [2, 3, 4]

What is a fold?

Fold (or reduce in some languages) takes a list, and returns a single value after going through each element in a list:

-- given f, a starting value for the aggregation x, 
-- and [a, b, c] -fold-> return f c (f b (f a x))

The method signature for foldl is:

foldl :: (b -> a -> b) -> b -> [a] -> b

A simple example of a fold is adding each number in a list of numbers with an initial sum of 0. It would look like this:

To reduce a list is to fold it

What to use?!

A map returns a list of the same length and fold returns a single value. But the function we need is supposed to return something that’s in-between — a list that’s smaller than the original, but not a single value.

Then I glanced at the method signature of foldl, again:

foldl :: (b -> a -> b) -> b -> [a] -> b

Nothing here is stopping b from being a list! b is just an aggregator. What if we were building a list, instead of say, a number, in our sum example? We could just call foldl like this:

foldl (\bs a -> bs ++ [a]) [] [1, 2, 3]
-- => [1, 2, 3]

Or if we wanted to filter the list so that it doesn’t have 2’s in it:

foldl (\bs a -> if a /= 2 then bs ++ [a] else bs) [] [1, 2, 3]
-- => [1, 3]

We expressed filter, in terms of fold. Here’s a named definition of filter using a fold:

Maps are Folds

But wait! We just performed a fold that builds a list of the same size as the input list (printed again below):

foldl (\bs a -> bs ++ [a]) [] [1, 2, 3]
-- => [1, 2, 3]

That’s almost our the definition of a map!

It’s not too much more work to apply any function, f, on the item before adding it to the list. That means we go through each item of the list and create another list with the result of calling f on each item. So here is a map defined using a fold:

Moral of the story: Folds are flexible

Basically a map is just a subset of a fold with a starting value of []. Anything you’ve written with a map could have been implemented with a fold. Makes you wonder what more a fold can do…

It’s so obvious now. How did I miss this?

After seeing the textbook examples of fold like sum, I subconsciously thought of fold-ing as a piece of paper folded into something smaller. But this comparison limited my understanding of the flexibility of fold (no pun intended).

I think the real lesson here is that the metaphors our minds create to understand things can just as easily limit our understanding. Be wary!

Impressed by the way Haskell teaches, I embarked on a flight that would fold the map bringing Yangon to Bangkok. (Sorry, I had to…)

--

--

Anirudh Eka

Passionate about finding patterns in computers, society and minds. Follow on twitter for free links! https://toomanynames.com/