An Emoji-Lover’s Guide to Functional Programming

Pipeline Stormy Clouds into Sunny Clouds

An advanced use of array map, array reduce, and functional composition using pipe

Kevin Ghadyani
ITNEXT
Published in
5 min readOct 11, 2017

--

Learn functional programming with Emojis and JavaScript. The code examples should be simple enough to figure out without any prior knowledge, but I can imagine it’ll look a little strange. Also, JavaScript doesn’t actually allow emojis as JavaScript variable names. For this reason, these code examples won’t run without modification.

We start with our add and subtract methods from last time:

add = additive => item => item + additive
subtract = subtractor => item => item - subtractor

We talked about using multiple map statements to change our stormy clouds to sunny clouds:

[⛅, 🌦️] = (
[🌩️, ⛈️️]
.map(subtract(⚡))
.map(add(☀️))
)

But did you know we could remove a map by composing add and subtract:

addSun = add(☀️)
removeLightning = subtract(⚡)
[⛅, 🌦️] = (
[🌩️, ⛈️️]
.map(item => (
removeLightning(
addSun(item)
)
)
)

Why would we do this? We could’ve just kept our two map statements and everyone would’ve been happy! Typically, that’s what you would do, but in some cases, you might need a different way to chain using either a compose or pipe. They perform the exact same functionality, except pipe takes parameters in reverse order similar to Polish notation:

compose = (second, first, item) => second(first(item))pipe = (item, first, second) => second(first(item))

Normally these return functions rather than taking item directly:

compose = (second, first) => item => second(first(item))pipe = (first, second) => item => second(first(item))

We’ll use pipe for our examples since it reduces the complexity by reading left-to-right.

Let’s assume we need to micro-optimize our code and two maps is too many. If we wanna get it down to one map, we’ll need to compose our functions. As we saw in the previous example, that can get ugly fast! Writing a simplified pipe function, we can accomplish our goal and keep the code clean:

pipe = (first, second) => item => second(first(item))[⛅, 🌦️] = [🌩️, ⛈️️].map(pipe(subtract(⚡), add(☀️)))

Now that we’ve figured out the basics of pipe, we’ll need to write a real one. That means we have to be able to give pipe an infinite number of functions for it to call in-order.

Functionally, this is simple using reduce, but without understanding the basics, it’s pretty tough to reason about. Let’s look at this procedurally:

item = nullupdateItem = change => {
item = change(item)
}
pipe = (...functions) => {
[item] = functions.splice(0, 1)

for(let i = 0, l = functions.length; i < l; i++) {
updateItem(functions[i])
}

return item
}
⛅ = pipe(☁️, subtract(⚡), add(☀️))

In this example, there’s no closure. We’re directly passing in the cloud emoji first then adding the rest of our functions as separate parameters.

First thing we do in our procedural pipe is remove the first argument. That’s going to be our item. We’ll loop the rest of the functions, calling them one by one and updating item to the latest value, until we’re done with the loop where we’ll return the modified item. Pretty messy.

This code has a lot of mutations. We’re creating item and then changing the value of item each time we loop somewhere else in the code, and updateItem is creating a side-effect.

There’s another problem here too: splice. This function mutates the array; pulling out a set of values and leaving the original array with the remaining values. Sure it makes sense right now when the code is small, but as you add in more pieces, it will be much harder to figure out the state of item and the functions array.

Mutations and side-effects are no longer needed with functional programming. By removing them, we end up writing pure functions which are far easier to unit test and multithread. Right now, the possibility of bugs is high. With pure functions, that possibility is greatly reduced.

Here’s the same pipe using immutability, functional composition, and reduce.

pipeReducer = (item, change) => change(item)pipe = (...functions) => {
[startingItem] = functions

return (
functions
.slice(1)
.reduce(pipeReducer, startingItem)
)
}
⛅ = pipe(🌩️, subtract(⚡), add(☀️))

Notice how we use slice (an immutable version of splice) so we don’t have to mutate our array of functions. The very first thing we do is use deconstruction to pull out startingItem from functions. Next, we use slice again, this time to take everything but the first item. We can then run reduce on our smaller array and call change(item) on one return value after another.

While reduce loops through each value in the array like map, each iteration gets both the previous value and the current one. The major difference is it ultimately returns a single value of any type. Also, if you don’t give reduce an initial value, it will default to using the first value in the array. That means we can simplify this even more:

pipeReducer = (item, change) => change(item)pipe = (...functions) => functions.reduce(pipeReducer)⛅ = pipe(🌩️, subtract(⚡), add(☀️))

This is looking really good now! Just like add and subtract, we’ll want to use closures so pipe can generate new functions. There are a few ways to write pipe. One is to explicitly pass our starting item as the initial value for reduce:

pipe = (...functions) => startingItem => (
functions
.reduce(pipeReducer, startingItem)
)

The other way is to add our startingItem to an array and concat our pipelined functions:

pipe = (...functions) => startingItem => (
[startingItem]
.concat(functions)
.reduce(pipeReducer)
)

It doesn’t matter which you choose. With pure functions, as long as the same inputs always get the same output, you can always refactor the inner function as much as you like. Using either of these methods, we’ll end up with the same result:

changeStormyToSunny = pipe(subtract(⚡), add(☀️))⛅ = changeStormyToSunny(☁️)

As a major benefit, we’ve suddenly got reusable weather changing code! Using our knowledge of closures, function generators, and pipe, we can refactor the double map into a single, readable map.

add = additive => item => item + additive
subtract = subtractor => item => item - subtractor
pipeReducer = (item, change) => change(item)
pipe = (...functions) => startingItem => (
functions
.reduce(pipeReducer, startingItem)
)
changeStormyToSunny = pipe(subtract(⚡), add(☀️))[⛅, 🌦️] = [🌩️, ⛈️️].map(changeStormyToSunny)

FEEL THE SIMPLICITY!

--

--