Home/Blog/Learn to Code/Understand JavaScript Currying in 7 Minutes
Home/Blog/Learn to Code/Understand JavaScript Currying in 7 Minutes

Understand JavaScript Currying in 7 Minutes

6 min read
Aug 05, 2019

Become a Software Engineer in Months, Not Years

From your first line of code, to your first day on the job — Educative has you covered. Join 2M+ developers learning in-demand programming skills.

Currying is a great tool that allows you to split arguments into individual functions. The main advantage is that it allow you to specialize or partially apply functions to then be passed to higher order functions, like map() and reduce().

Today, we’ll show you how to use currying in your own programs.

We’ll cover:


Master Functional Programming

Learn functional programming with hands-on practice.

Functional Programming Patterns With RamdaJS



What is currying?#

The definition of currying is:

Currying turns multi-argument functions into unary (single argument) functions.

Curried functions take many arguments one at a time.

In other words, In other words, the function takes the first argument and returns a new function. This new function takes the second argument and returns a new function which then takes the third argument. This repeats until all arguments have been fulfilled.

Currying is helpful for functional programming as it allows a short hand syntax for this common functional procedure.

Let’s see an example:

greet = (greeting, first, last) => `${greeting}, ${first} ${last}`;
greet('Hello', 'Bruce', 'Wayne'); // Hello, Bruce Wayne

Properly currying greet gives you:

curriedGreet = curry(greet);
curriedGreet('Hello')('Bruce')('Wayne'); // Hello, Bruce Wayne

This 3-argument function has been turned into three unary functions. Currying returns a function for each passed parameter.


Ways to implement currying#

I say “properly currying” because some curry functions are more flexible in their usage. Currying’s great in theory, but invoking a function for each argument gets tiring in JavaScript.

Ramda’s curry function lets you invoke curriedGreet like this:

// greet requires 3 params: (greeting, first, last)
// these all return a function looking for (first, last)
curriedGreet('Hello');
curriedGreet('Hello')();
curriedGreet()('Hello')()();
// these all return a function looking for (last)
curriedGreet('Hello')('Bruce');
curriedGreet('Hello', 'Bruce');
curriedGreet('Hello')()('Bruce')();
// these return a greeting, since all 3 params were honored
curriedGreet('Hello')('Bruce')('Wayne');
curriedGreet('Hello', 'Bruce', 'Wayne');
curriedGreet('Hello', 'Bruce')()()('Wayne');

Notice you can choose to give multiple arguments in a single shot. This implementation’s more useful while writing code.

And as demonstrated above, you can invoke this function forever without parameters and it’ll always return a function that expects the remaining parameters.

Mr. Elliot shared a curry implementation much like Ramda’s. Here’s the code, or as he aptly called it, a magic spell:

const curry = (f, arr = []) => (...args) =>
((a) => (a.length === f.length ? f(...a) : curry(f, a)))([...arr, ...args]);

It’s incredibly concise, so let’s refactor and appreciate it together.


The version below works the same as the dense implementation above. I’ve also sprinkled debugger statements to examine it in Chrome Developer Tools.

curry = (originalFunction, initialParams = []) => {
debugger;
return (...nextParams) => {
debugger;
const curriedFunction = (params) => {
debugger;
if (params.length === originalFunction.length) {
return originalFunction(...params);
}
return curry(originalFunction, params);
};
return curriedFunction([...initialParams, ...nextParams]);
};

Open your Developer Tool sand follow along!


Currying tutorial#

To get started, paste greet and curry into your console. Then enter curriedGreet = curry(greet) to begin.

widget

Inspecting our two parameters we see originalFunction is greet and initialParams defaulted to an empty array because we didn’t supply it.

curry(greet) just returns a new function that expects 3 more parameters. Type curriedGreet in the console to see the the new function.

Now that we’ve seen the basic function, let’s get a bit more advanced and dosayHello = curriedGreet('Hello').

widget

To see the original function and parameters, type originalFunction and initialParams in your console.

Notice we can still access those 2 parameters even though we’re in a completely new function? That’s because functions returned from parent functions enjoy their parent’s scope.

After a parent functions can leave their parameters for their child functions to use. Kind of like inheritance in the real life sense.

curry was initially given originalFunction and initialParams and then returned a “child” function.

Those 2 variables are not disposed in case the child function needs to reference them. If they don’t, then that scope gets cleaned up and the parameters are deleted.


Curry’s use of REST syntax#

On line 4, put a debugger to check the status of the program. This will help us understand how curry handles parameters.

widget

Inspect nextParams and see that it’s ['Hello']…an array? But I thought we said curriedGreet(‘Hello’) , not curriedGreet(['Hello'])!

Correct: we invoked curriedGreet with 'Hello', but thanks to the REST syntax, 'Hello' becomes into ['Hello'].

This is because curry is a general function that can be supplied 1, 10, or 10,000,000 parameters, so it needs a way to reference all of them. Using the REST syntax like that captures every single parameter in one array, making curry’s job much easier.

Let’s jump to the next debugger statement.

You may have noticed that line 12 actually ran before the debugger statement on line 6. Our program defines a function called curriedFunction on line 5, uses it on line 12, and then we hit that debugger statement on line 6.

What is curriedFunction invoked with?

[...initialParams, ...nextParams];

Look at params on line 5 and you’ll see ['Hello']. Both initialParams and nextParams were arrays, so we flattened and combined them into a single array using the handy spread operator (...).

Take a look:

widget

Line 7 says “If params and originalFunction are the same length, call greet with our params and we’re done.”


How does curry use .length()?#

This is how curry does its magic! Curry uses the output from .length() to know when it must ask for more parameters.

In JavaScript, a function’s .length property tells you how many arguments it expects. If there are less arguments (lower length) than expected, curry will ask for more arguments.

greet.length; // 3
iTakeOneParam = (a) => {};
iTakeTwoParams = (a, b) => {};
iTakeOneParam.length; // 1
iTakeTwoParams.length; // 2

If our provided and expected parameters match, we’re good, just hand them off to the original function and finish the job!

Our example has less than the desired number of arguments.

We only provided ‘Hello’, so params.length is 1, and originalFunction.length is 3 because greet expects 3 parameters: greeting, first, last.

Since that if statement evaluates to false, the code will skip to line 10 and re-invoke our master curry function. It re-receives greet and this time, 'Hello', and begins all over again.

curry is essentially an infinite recursive loop of self-calling, parameter-hungry functions that won’t rest until the function has enough parameters.

widget

Next recursive pass#

Same parameters as before, except initialParams is ['Hello'] this time. Skip again to exit the cycle. Type our new variable into the console, sayHello. It’s still expecting more parameters but we’re getting closer to our exit condition.

Next we’ll try it with sayHelloToJohn = sayHello('John').

We’re inside line 4 again, and nextParams is ['John']. Jump to the next debugger on line 6 and inspect params: it’s ['Hello', 'John']!

widget

Combining past parameters#

Line 12 says “Hey curriedFunction, he gave me 'Hello' last time and ‘John’ this time." It then combines both into this array using the spread operator, [...initialParams, ...nextParams].

widget

Now curriedFunction again compares the length of these params to originalFunction, and since 2 < 3 we move to line 10 and call curry once again! And of course, we pass along greet and our 2 params, ['Hello', 'John'].

widget

Next we pass the final parameter:

sayHelloToJohnDoe = sayHelloToJohn('Doe')

This is added to the array with the previous parameters. By this point, we have the 3 we need and our exit condition triggers.

widget
widget
widget
widget

Wrapping up#

greet got his parameters, curry stopped looping, and we’ve received our greeting: Hello, John Doe.

Play around with this function some more. Try supplying multiple or no parameters in one shot, get as crazy as you want. See how many times curry has to recurse before returning your expected output.

curriedGreet('Hello', 'John', 'Doe');
curriedGreet('Hello', 'John')('Doe');
curriedGreet()()('Hello')()('John')()()()()('Doe');

Many thanks to Eric Elliott for introducing this to me, and even more thanks to you for appreciating curry with me. Until next time!

If you’d like more information on currying, you can visit Educative’s course Functional Programming Patterns with RamdaJS which gives you an in-depth look at currying, its advantages, and much more.

Happy learning!


Continue reading about Functional programming and JavaScript#


Written By:
Yazeed Bzadough
 
Join 2.5 million developers at
Explore the catalog

Free Resources