Currying in javascript

This article is a work in progress

What is currying?

Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument.

So currying transforms fn(a, b, c…n) to fn(a)(b)(c)…n) . Let’s dive right in to an example.

const sum = (a, b) => a + b

const curriedSum = curry(sum)

console.log(curriedSum(1)(2)) // = 3

The curriedSum function converts the sum function into a sequence of two functions since the original sum function needs two arguments.

Naive implementation

Based on the above example, we can implement a naive version of the curry function. So curriedSum returns a function which accepts one argument b. This is passed as the second argument to the function being curried. The first argument is stored in the function closure.

function curry(fn) {
	return function (a) {
		return function (b) {
			return fn(a, b)
		}
	}
}

Currying with variadic arguments

Th previous implementation has a few problems

  1. It only works for functions which accept 2 arguments.
  2. The context value is not being propagated accurately.

Let’s try implementing a generic currying function which supports variadic arguments.

There are a couple of key insights behind the new implementation.

  1. Function.length returns the arity of the function. The arity of a function is the number of parameters expected by the function.

For example, consider this multiply function which returns the product of three numbers;

const multiply = (a, b, c) => a * b * c
console.log(multiply.length) // 3
  1. We have to keep collecting arguments until we have enough arguments, i.e. till the number of arguments are greater than or equal to the arity of the function.
function curry(fn) {
    return function curried(...args) {
        if(args.length >= fn.length) { // Execute the original function when we have enough arguments
            return fn.apply(this, args)
        } else {
            return (...nextArgs) => curried(...args, ...nextArgs) // otherwise keep on adding arguments
        }
    }
}

We can use bind for a more concise implementation.

function curry(fn) {
  return function curried(...args){
    if(args.length >= fn.length) {
      return fn.apply(this, args)
    } else {
      return curried.bind(this, ...args)
    }
  }
}

Infinite sum

This happens to be a popular interview question based on recursion. So here’s the infinite sum function that we have to implement

sum(1)() // 1
sum(3)(2)() // 5
sum(1)(2)...(n)() // (1 + 2 + 3 ...n)

We can make some observations based on the above example

  1. The sum function returns the sum when we call it with undefined.
  2. If the function is called with a valid number another function is returned
const sum = a => {
    return b => {
        if(b === undefined) {
            return a
        } else {
            return sum(a + b)
        }
    }
}

Infinite sum with variadic arguments

sum(1, 2)(3)(4,5)() //15

This is a bit more tricky. We are not dealiing with single arguments anymore.Try implementing this on your own before checking the solution.

const sum = (...args) => {
  return (...moreArgs) => {
    if (moreArgs.length === 0) {
      return args.reduce((a, b) => a + b);
    } else {
      return sum(...args, ...moreArgs);
    }
  };
};

Currying with placeholder support

We are not done with currying yet, currying with placeholder support is something I might tackle in the future.

Subscribe to the newsletter

Get updates about new posts, new projects and more delivered to your inbox. Alternatively, you can follow me on Twitter.