Promises and Arrays are the same

Jamie Dixon
4 min readJun 2, 2016

--

I like things to be simple. I’m the kind of person that gets confused and overwhelmed with lots of detail so I like to get down to the core of things, the premises and the underlying concepts.

Understanding the basic philosophies that underpin the things we do simplifies swathes of complex and complicated ideas.

One of the tools we have for limiting our focus and simplifying the things we observe is to impose constraints on how we look. In the sciences, observer-constraints are applied to observations in order to see differently.

By looking at light as waves we will be presented with one set of data, and by observing light as particles we can receive different data. These constraints allow us to focus our attention and through that, to come to understand the things we’re looking at in different ways.

The same can be said of many concepts in functional programming. Monads, Comonads, Monoids, Setoids and Functors. As well as being fancy-shmancy words we can talk about these as ways to constrain how we look at our code. They’re like a pair of old-skool 3D glasses either letting through the red light or the green light.

The words look complicated but when we notice that they’re just ways of looking at things, sets of rules, they become simple, and it’s through one of these lenses that I want to compare Arrays and Promises.

Functor — Don’t you use that language with me!

For a long time I’d felt that Arrays and Promises were very similar but I hadn’t been able to articulate why or how.

Through the lens of the Functor we can begin our exploration into the similarities between the two data structures but first up…

What the fun is a functor?

An object with a map function that adheres to certain rules. map runs a function on values in a container and returns a new container of the same type.

Let’s explore the rules that make a functor a functor and apply them first to Arrays and then to Promises.

Rule 1: The result of mapping without transformations must yield a result that is the same as the object we mapped.

We can express with with a rule that looks like:

functor.map(x => x) == functor.

What happens when we apply this rule to Arrays?

When we map the value of an array and return it without any transformations, we get back another array who’s values are exactly the same.

What about Promises? They don’t even have a map function!

Luckily for us, map doesn’t have to be called “map” to fulfill our criteria. We simply need a function that conforms to our definition and in the case of our Promise, that function is `then`.

Array.prototype.map and Promise.prototype.then are both methods that unwrap the values we store inside the respective containers and both re-wrap those values at the end. Arrays map to arrays and Promises map to Promises.

Rule 2: The result of composing transformations over the value must be equal to mapping the transformations without composition.

That sounds complicated but it really just means that a map(x => f(g(x))) must equal map(g).map(f).

Let’s first look at how this plays our with Arrays:

and now with Promises:

Again we see that both Arrays and Promises conform to this rule.

These are the only rules that determine whether something is a functor or not. By conforming to these rules both Arrays and Promises are therefore functors.

That’s cool but how is this knowledge useful?

There’s a lot of confusion about Promises and many people think of them as black boxes where async happens and it’s all magic.

But magic doesn’t help us here. It doesn’t help us gain clarity and so the purpose of this experiment is to limit our scope of vision, to reduce our complex problem down into something that can offer us value.

Through the lens of functors we can see that Promises, like Arrays, are containers for values. In the case of Arrays, the values are present-values and in the case of Promises they may be future-values. But they’re both containers for values that can be mapped over, transformed and returned back to us in the form we expect.

We might also change our view on what it means to map an array. Plenty of people read array.map as “foreach”. Through our understanding of functors we can see that map isn’t foreach at all. It’s purpose is to unwrap the values contained in our type. It just so happens that an array is a type with many values, each of which gets presented to our mapping function.

Looking at different aspects of life and programming through different lenses and filters helps us come to new understandings and keeps us from falling into the traps of truth.

There are plenty of differences between Arrays and Promises, just as there are plenty of differences between you and me. But when we take a moment to look through the tunnel of sameness, we find useful patterns that help us navigate both the real world and our programatic worlds to understand more clearly and to appreciate differently.

--

--