Currying and Function Composition in Javascript

CodeWithAL
The Startup
7 min readSep 10, 2019

--

As we’re hearing more and more about functional programming in mainstream JavaScript scene, curried functions have become way more common in many applications. It’s very fundamental to have a solid grasp regarding what they are, how they work, and how to put them for a best-practice way of use.

Interesting fact: Currying is named after a mathematician Haskell Curry, not the food :)

What is a curried function?

There is a lot of computer science and mathematical theory behind currying in functional programming but if we put it together in the abstract level, currying is a concept that involves splitting up a function that takes multiple arguments into a sequence of functions that each take an individual argument.

But before we get started, let’s talk briefly about functions in JavaScript. There are two ways to define JS functions: declarations and expressions.

// function declaration 
function addOne(foo) {
return foo + 1
}
// function expression
var addOne = function(foo) {
return foo + 1
}

Function declarations are named functions invoked with the function keyword, while function expressions are anonymous functions that are assigned to a variable(*let, const, var). The most important difference between them is about hoisting. In JavaScript, variables(*They are all hoisted to the top of their scope but while varvariables are initialized with undefined, let and const variables are not initialized.) and function statements are hoisted to the top of their scope, which means they can be interacted with before being declared.

Function declarations load before any code is executed while Function expressions load only when the interpreter reaches that line of code.

Introducing Arrow Functions

Arrow functions are a short way of creating function expressions introduced via ES6. I will stick to using const while creating arrow functions rather than avar, this isn't absolutely necessary, but in general using const with function expressions that you don't intend to reassign is a good practice in ES6.

Arrow functions do a number of things automatically behind the scenes, like lexically binding this (which is very important!).

// function expression 
var addOne = function(foo) {
return foo + 1
}
// equivalent arrow function
const addOne = (foo) => foo + 1
// equivalent arrow function with optional syntax added
const addOne = (foo) => {
return foo + 1
}

Handling Complexity

A function that basically increasing a number by one is not a big deal right? So let’s make things a bit more complicated for further investigating the topic of the article and get more into function composition.

const addOneToEach = arr => arr.map(num => num + 1)addOneToEach([1, 2, 3]) // returns [2, 3, 4]

So we’ve declared an arrow function and an array. Our function addOneToEach takes an array as it’s only parameter. Iterates over the array by incrementing each number in the array by 1 and returns [2, 3, 4].

Array.prototype.map(). .map() takes a callback function with the arguments (currentValue, index, array). But in our case we only cared about currentValue

That’s a good start! But what if we were using an API, and dealing with arrays provided via the API. Not all API’s are great (actually just a few). The data we will be retrieving will not be consistent in most cases, it can be numbers or can be strings. We’ll have to add some logic to detect strings and un-stringify them.

const addOneToEach = arr => arr.map(data => {
if (typeof data === "string") {
return parseInt(data) + 1
}
return data + 1
})
addOneToEach([1, 2, 3]) // returns [2, 3, 4]
addOneToEach([1, "2", 3]) // returns [2, 3, 4]

That works, but things started to get a b it messy tho. We’ve already started nesting logic three levels deep.

One trick for writing clean arrow functions when you have an if...else logic block is to use a ternary expression instead, so let's try that.

const addOneToEach = arr => 
arr.map(data => typeof data === "string"
? parseInt(data) + 1
: data + 1
)

Good! But we can’t reuse our well-thought logic anywhere, and we can’t export it or test it in isolation. The best-practice in this case would be splitting up the + 1 and parseInt into separate named functions and use those as separate callbacks inside chained arr.map() calls.

const ensureNum = data => 
typeof data === "string" ? parseInt(data) : data
const addOne = num => num + 1const addOneToEach = arr => arr.map(ensureNum).map(addOne)

Well done! We’ve splitted the logic into sub logics and created individual and compact arrow functions; ensureNum and addOne, which makes them easily reusable, exportable, and testable. We use these functions as callbacks in a chained .map() call in our addOneToEach function.

But what if we wanted to introduce some new features that will require incrementing by three and ten sometimes, not just one. How do we implement that?

const ensureNum = data =>   
typeof data === "string"
? parseInt(data)
: data
const addOne = num => num + 1
const addThree = num => num + 3
const addTen = num => num + 10

const addOneToEach = arr => arr.map(ensureNum).map(addOne)
const addThreeToEach = arr => arr.map(ensureNum).map(addThree)
const addTenToEach = arr => arr.map(ensureNum).map(addTen)

I mean of course we can do it the hard way simply coding alternative functions such as addTheToEach addTenToEach but we don’t want to do that, we want to do it in the DRY way! let’s add a new param, by, to hold the number we are incrementing by.

const ensureNum = data =>   
typeof data === "string" ? parseInt(data) : data
const incrementEach = (arr, by) =>
arr.map(ensureNum)
.map(num => num + by)

That’s much better, but we’ve lost our named addOne callback. Let's make a new addNums arrow function that we can pass both the current num in .map() and the new by param from incrementEach to.

const ensureNum = data=> 
typeof data === "string" ? parseInt(data) : data
const addNums = (a, b) => a + bconst incrementEach = (arr, by) =>
arr.map(ensureNum)
.map(num => addNums(num, by))

Let’s add the curry that I have been talking about!

Let’s just forget about what we’ve been trying to do so far for a little break and talk about currying in action!

//Using function declaration syntax
function add(a, b){
return a + b;
}
add(2, 3); // returns 5//Using arrow functions syntax
const add = (a, b) => {
return a + b;
}
add(2, 3); // returns 5

Now we have a function that takes two arguments, a and b, and returns the sum of a and b. Now we are going to curry this function:

//Using function declaration syntax
function add (a) {
return function (b) {
return a + b;
}
}
//Using arrow functions syntax
const add = a => b => a+b;

As you can see we have a function named add that takes one argument, a, and returns an anonymous function that takes another argument, b, and returns the sum of a and b.

I got you! Don’t worry about the double arrow function at all! If you break it down, you have a named arrow function returning an anonymous arrow function, and both functions are accepting a single parameter.

add(2)(3);const add2 = add(2);add2(3);

Above, the first statement returns 5, like the add(2, 3) statement. The second statement defines a new function called add2 that will add 2 to its argument. This is what some people may call a closure. The third statement uses the add2 function to add 3 to 2 to produce 5 as a result.

Or just for added complexity and demostration purposes we can chain another function for triple effect…

//Using function expression syntaxvar sum = function(x, y, z){
return x + y + z;
}
console.log(sum(1, 2, 3)); // 6
var sum = function sum(x) {
return function(y) {
return function(z) {
return x + y + z;
};
};
};
console.log(sum(1)(2)(3)); // 6
//Using arrow functions syntax//Using arrow functions syntax
const sum = (x, y, z) => x + y + z;
console.log(sum(1, 2, 3)); // 6
const sum = x => y => z => x + y + z;
console.log(sum(1)(2)(3)); // 6

As you can see in the most fundamental level, nothing changes.

We’re back at where we’ve left

Let’s go back to our incrementEach function. Here is where we left things:

const ensureNum = data=> 
typeof data === "string" ? parseInt(data) : data
const addNums = (a, b) => a + bconst incrementEach = (arr, by) =>
arr.map(ensureNum)
.map(num => addNums(num, by))

Now let’s write them again with currying!

const ensureNum = data =>   
typeof data === "string" ? parseInt(data) : data
const addNums = a => b => a + bconst incrementEach = (arr, by) =>
arr.map(ensureNum)
.map(addNums(by))

addNums is now a function that takes a parameter that returns a function that takes another parameter. So first we call addNums(by), which returns the anonymous function b => by + b. .map() then calls this anonymous function as its callback and returns by + currentValue — the current value in the array incremented by the value of by.

With currying and (double) arrow functions, we get to keep our named callbacks that are exportable, reusable, and testable in isolation. We get the handy callback shorthand syntax so we don’t have to write extra anonymous callback functions just to pass variables, yet our named callback functions are still flexible enough to receive params passed down from a higher-order function when they are called.

I hope you enjoyed the read and maybe even learned a few things out of it or just remembered back forgotten information.

--

--