Currying in javascript

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.

Buy me a coffee ☕