Variadic currying and currying to fixed arity in JavaScript

Different types of currying and how to use them for problem solving

Ivan Korolenko
Jun 28, 2020 · 5 min read

In this article I will tell you about different types of currying in JavaScript. To understand them, you will need to know about classical currying. You can read my article on that topic and then proceed with this one.

I don’t need the whole world, just you

Let’s look at the example of a curry function in JavaScript:

const curry = (fn) =>     
function curried(...args) {
const haveEnoughArgs = args.length >= fn.length
const partiallyApplied = (...moreArgs) =>
curried(...args.concat(moreArgs))

if (haveEnoughArgs) return fn(...args)
return partiallyApplied
}

It returns a function that can handle cases like this:

  • curry(someFunction(1)(2)(3))
  • curry(someFunction(1, 2, 3))
  • curry(someFunction(1, 2)(3))
  • curry(someFunction(1)(2))

What we would like to add to this list?

Well, what if we want to call our curried function with less/more arguments than its requiring?

For example, we have a function that can add up numbers infinitely:

const addInfinite = (...args) => args.reduce((acc, cur) => acc + cur, 0)

We want to curry it and then add up numbers one or more at a time from some infinite stream. We don’t know when we’ll run out of numbers to add, but we know that we only need 10 of them. Let’s say, we are trying to compute some statistical indicator in real-time. So we need to update it as soon as possible and 10 data-points will be enough.

Cool. Let’s try it. We curry and feed it our first number from a stream:

curry(addInfinite)(5) // returns 5

Wait, it returned a value instead of a partially applied function. Now we can’t feed it other numbers. But why?

JavaScript registers our function’s arity as 0 since it can take any number of arguments. So, if we curry it with our classical curry function and call it with anything, it returns right away. Even like this:

curry(addInfinite)() // returns 0

How do we fix this?

We can use currying to fixed arity!

Currying to fixed arity returns a curried function that expects a certain number of arguments to return a value.

So now:

curryN(10, addInfinite)(5)(2)(8) // returns partially applied function
curryN(10, addInfinite)(5)(2)(8)(5)(2)(1)(8)(3)(4)(5) // returns 43

Great, we got what we wanted!

Now let’s see how to write this function. Well, it will be easy. Currying to a fixed arity is the same as classical currying, but instead of getting desired arity from the function itself we just set it explicitly.

Basically, classical currying is a special case of currying to fixed arity.

And we already have the code for classical arity. So, we just change it a bit:

const curryN = (n, fn) =>    
function curried(...args) {
// compare to n instead of fn.length
const haveEnoughArgs = args.length >= n
const partiallyApplied = (...moreArgs) =>
curried(...args.concat(moreArgs))

if (haveEnoughArgs) return fn(...args)
return partiallyApplied
}

That’s it! We have ourselves a new function that does currying to a fixed arity.

And since we have established classical currying as a special case of that mechanism, we can now rewrite our classical curry function and get rid of code duplication:

export const curry = (fn) => curryN(fn.length, fn)

Nice!

Stop button

Now, let’s look at another problem. Let’s suppose we have this function:

const addFourOrLessNumbers = (a, b, c, d) 
=> (a || 0) + (b || 0) + (c || 0) + (d || 0)

It can add up any number of values up to 4. If we call it like this

addFourOrLessNumbers(1, 2)

it will return 3, as expected. JavaScript did its magic and replaced variables ‘c’ and ‘d’ with undefined value. So it’s calculated properly. But now let’s curry it using classical curry function and then call it with same arguments:

curry(addFourOrLessNumbers)(1, 2) // returns function

We expect to get 3, but got a partially applied function. Why? Because curried function expects all of its arguments before it can be executed.

Okay, as we’ve learned above, we can use curryN for this purpose.

curryN(2, addFourOrLessNumbers)(1, 2) // returns 3

It works. But what if we need to call this function with 2, 3 or 4 in the same place depending on some condition. Of course, we can make separate function for each such case using curryN, but it’s not really handy, right? And imagine similar function, but with 10 or more arguments.

const addTwoOrLessNumbers = curryN(2, addTenOrLessNumbers)
const addThreeOrLessNumbers = curryN(3, addTenOrLessNumbers)
const addFourOrLessNumbers = curryN(4, addTenOrLessNumbers)
...
if (someCondition && numbers.length === 2)addTwoOrLessNumbers(...)
else if (someCondition && numbers.length === 3)
addThreeOrLessNumbers(...)
...

Not cool.

So, classical curry can’t help us here, as well as curryN. What do we do?

Has functional world failed us? Should we just accept it, surrender to the harsh reality, rethink our lives and move on?

No! Because we have variadic currying! So we can just do this:

const curriedAddFourOrLessNumbers = curryV(addFourOrLessNumbers)
curriedAddFourOrLessNumbers(1)(3)
if (someCondition)curriedAddFourOrLessNumbers() // returns 4

Cool!

Variadic currying is a currying that allows currying of a variadic function through termination.

Termination in currying is a process of calling a curried function before it received all of its arguments. It works by calling a curried function with an empty argument.

Do not confuse termination and partial application. The latter returns a function with smaller arity while the former returns a value of a function.

Now let’s write variadic currying function:

const curryV = (fn) =>         
function curried(...args) {
let shouldRunRightNow = args.length === 0
const haveEnoughArgs = args.length >= fn.length
const partiallyApplied = (...moreArgs) => {
shouldRunRightNow = moreArgs.length === 0
if (shouldRunRightNow) return fn(...args)
return curried(...args.concat(moreArgs))
}

if (haveEnoughArgs || shouldRunRightNow) return fn(...args)
return partiallyApplied
}

As you can see, it’s pretty similar to classic currying, but more powerful.

Variadic currying = classical currying + ability to get value from curried function on demand (termination).

It’s very useful in cases with variadic functions (functions with varying arity).

If the content of this article seems too heavy, you might want to refresh your understanding of classical currying and then return here.

If you have any questions, feel free to leave a comment here or contact me through my website (link in my Medium profile -> “How to contact me?”). I’ll be happy to help you.

You can just use the library

If you want to use these instruments, you don’t need to write all of that by hand, cover it with tests, add documentation and wrap it in a library.

I’ve already done it for you. Here’s the npm package and GitHub.

That’s all!

I hope, this journey was interesting.

If you have any questions, feel free to leave a comment here or contact me through my website (link in my Medium profile -> “How to contact me?”). I’ll be happy to help you.

Thank you for reading!

The Startup

Get smarter at building your thing. Join The Startup’s +788K followers.

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Ivan Korolenko

Written by

https://ivankorolenko.com | Senior front end engineer | Turning wishes into software

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +788K followers.

Ivan Korolenko

Written by

https://ivankorolenko.com | Senior front end engineer | Turning wishes into software

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +788K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store