In this chapter we are going to see what the term currying
means. Once we understand what this means and where it can be used, we will move onto another concept in functional programming called partial application. Both currying and partial application are important to understand as we use them during functional composition. As in the previous chapters, we are going to look at a sample problem and explain how functional programming techniques like currying and partial application can be applied.
A Few Notes on Terminology
Before explaining what currying and partial application mean, we need to understand a few terms that we will be using in this chapter.
Unary Function
A function is called
unary if it takes a single
function argument. For example, the
identity function, shown in Listing
6-1, is a unary function.
const identity = (x) => x;
Listing 6-1Unary identity Function
This function takes only one argument, x, so we can call it a unary function.
Binary Function
A function is called
binary if it takes two
arguments. For example, in Listing
6-2, the
add function is a binary function.
const add = (x,y) => x + y;
Listing 6-2Binary add Function
The add function takes two arguments, x,y; hence we call it a binary function.
As you can guess, there are ternary functions that take three arguments, and so on. JavaScript also allows a special type of function that we call a variadic function, which takes a variable number of arguments.
Variadic Functions
A
variadic function is a function that takes a variable number of arguments. Remember that we had
arguments in older versions of JavaScript, which we can use to capture the variable number of arguments.
function variadic(a){
console.log(a);
console.log(arguments)
}
Listing 6-3Variadic Function
We call the
variadic function like this:
variadic(1,2,3)
=> 1
=> [1,2,3]
As you can see in Listing
6-3, using
arguments we are able to capture the additional arguments one could call on a function. Using this technique, we used to achieve the variadic functions in ES5 versions. However, starting with ES6, we have an operator called
Spread Operator
that we can use to achieve the same result.
const variadic = (a,...variadic) => {
console.log(a)
console.log(variadic)
}
Listing 6-4Variadic Function Using Spread Operator
Now if we call this function we get exactly what we would expect:
variadic(1,2,3)
=> 1
=> [2,3]
As you can see in the result, we were pointed to the first passed argument 1 and all other remaining arguments captured by our variadic variable that uses the ... rest argument! ES6 style is more concise as it clearly mentions that a function does take variadic arguments for processing.
Now that we have some common terms in mind with respect to functions, it’s time to turn our attention to the fancy term currying.
Currying
Have you heard the term currying n number of times from the blogs and still wonder what it means? Don’t worry; we are going to break the currying definition into smaller definitions, which will make sense to you.
We’ll start with a simple question: What is currying? A simple answer to that question would be this: Currying is a process of converting a function with n number of arguments into a nested unary function. Don’t worry if that doesn’t make sense to you yet. Let’s see what it means using a simple example.
Imagine we have a function called
add:
const add = (x,y) => x + y;
It’s a simple function. We can call this function like
add(1,1), which is going to give the result
2. Nothing fancy there. Now here is the curried version of the
add function:
const addCurried = x => y => x + y;
The
addCurried function
is now a curried version of
add. If we call
addCurried with a single argument like this:
it returns a function where
x value is captured via the closure concept as we saw in earlier chapters:
We can call the
addCurried function
like this to get the proper result:
Here we have manually converted the
add function, which takes the two arguments into an
addCurried function, which has nested unary functions. The process of converting a function from two arguments to a function that takes one argument (unary function) is called currying, as shown in Listing
6-5.
const curry = (binaryFn) => {
return function (firstArg) {
return function (secondArg) {
return binaryFn(firstArg, secondArg);
};
};
};
Listing 6-5curry Function Definition
Now we can use our
curry function to convert the
add function to a curried version like this:
let autoCurriedAdd = curry(add)
autoCurriedAdd(2)(2)
=> 4
The output is exactly what we wanted to get. Now it’s time to revise the definition of currying: Currying is a process of converting a function with n number of arguments into a nested unary function.
As you can see in our curry function definition, we are converting the binary function into nested functions, each of which takes only one argument; that is, we are returning the nested unary functions. Now we have clarified the term currying in your head, but the obvious questions you still have are these: Why do we need currying? What is its use?
Currying Use Cases
We’ll start simple. Imagine we have to create a function for creating
tables. For example, we need to create
tableOf2,
tableOf3,
tableOf4, and so on. We can achieve this via Listing
6-6.
const tableOf2 = (y) => 2 * y
const tableOf3 = (y) => 3 * y
const tableOf4 = (y) => 4 * y
Listing 6-6tables Function Without Currying
With that in place, the functions can be called this:
tableOf2(4)
=> 8
tableOf3(4)
=> 12
tableOf4(4)
=> 16
Now you see that you can generalize the tables concept into a single
function like this:
const genericTable = (x,y) => x * y
and then you can use
genericTable to get
tableOf2 like the following:
genericTable(2,2)
genericTable(2,3)
genericTable(2,4)
and the same for
tableOf3 and
tableOf4. If you notice the pattern, we are filling up
2 in the first argument for
tableOf2,
3 for
tableOf3, and so on! Perhaps you are thinking that we can solve this problem via
curry? Let’s build tables from
genericTable using
curry:
const tableOf2 = curry(genericTable)(2)
const tableOf3 = curry(genericTable)(3)
const tableOf4 = curry(genericTable)(4)
Listing 6-7tables Function Using Currying
Now you can do your testing with these curried versions of the
tables:
console.log("Tables via currying")
console.log("2 * 2 =",tableOf2(2))
console.log("2 * 3 =",tableOf2(3))
console.log("2 * 4 =",tableOf2(4))
console.log("3 * 2 =",tableOf3(2))
console.log("3 * 3 =",tableOf3(3))
console.log("3 * 4 =",tableOf3(4))
console.log("4 * 2 =",tableOf4(2))
console.log("4 * 3 =",tableOf4(3))
console.log("4 * 4 =",tableOf4(4))
This is going to print the value we expect:
Table via currying
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
3 * 2 = 6
3 * 3 = 9
3 * 4 = 12
4 * 2 = 8
4 * 3 = 12
4 * 4 = 16
A logger Function: Using Currying
The
example in the previous section helped us understand what currying does, but let’s use a more complicated example in this section. As developers when we write code, we do a lot of logging at several stages of the application. We could write a helper
logger function that looks like Listing
6-8.
const loggerHelper = (mode,initialMessage,errorMessage,lineNo) => {
if(mode === "DEBUG")
console.debug(initialMessage,errorMessage + "at line: " + lineNo)
else if(mode === "ERROR")
console.error(initialMessage,errorMessage + "at line: " + lineNo)
else if(mode === "WARN")
console.warn(initialMessage,errorMessage + "at line: " + lineNo)
else
throw "Wrong mode"
}
Listing 6-8Simple loggerHelper Function
When any developer needs to print an error to the console from the
Stats.js file, he or she can use the function like the following:
loggerHelper("ERROR","Error At Stats.js","Invalid argument passed",23)
loggerHelper("ERROR","Error At Stats.js","undefined argument",223)
loggerHelper("ERROR","Error At Stats.js","curry function is not defined",3)
loggerHelper("ERROR","Error At Stats.js","slice is not defined",31)
Similarly, we can use the loggerHelper function
for debug and warn messages. As you can tell, we are repeating the arguments, mainly mode and initialMessage
, for all the calls. Can we do it better? Yes, we can do these calls better via currying. Can we use our curry function that is defined in the previous section? Unfortunately, no, because the curry function that we have designed can handle only binary functions, not a function like loggerHelper that takes four arguments.
Let us solve this problem and implement the fully functional curry function, which handles any function with n number of arguments.
Revisit Curry
We all know that we can
curry
(Listing
6-5) only a function. How about many functions? It’s simple but important to have it in our
implementation of
curry. Let’s add the rule first, as shown in Listing
6-9.
let curry =(fn) => {
if(typeof fn!=='function'){
throw Error('No function provided');
}
};
Listing 6-9Revisting curry Function Definition
With that check in place, if others call our
curry function with an integer like
2, and so on, they get back the error. That’s perfect! The next requirement to our curried function is that if anyone provided all
arguments to a curried function, we need to execute the real function by passing the arguments. Let’s add that using Listing
6-10.
let curry =(fn) => {
if(typeof fn!=='function'){
throw Error('No function provided');
}
return function curriedFn(...args){
return fn.apply(null, args);
};
};
Listing 6-10curry Function Handling Arguments
Now if we have a
function called
multiply:
const multiply = (x,y,z) => x * y * z;
We can use our new
curry function like the following:
curry(multiply)(1,2,3)
=> 6
curry(multiply)(1,2,0)
=> 0
Let’s look at how it really works. We have added the logic in our
curry function like this:
return function curriedFn(...args){
return fn.apply(null, args);
};
The returned function is a
variadic function
, which returns the function result by calling the function via
apply along by passing the
args:
. . .
fn.apply(null, args);
. . .
With our
curry(multiply)(1,2,3) example,
args will be pointing to
[1,2,3] and
because we are calling
apply on
fn, it’s equivalent to:
which is exactly what we wanted! Thus, we get back the expected result from the function.
Now let us get back to the problem of converting the
n argument function into a nested unary
function (that’s the definition of
curry itself)!
let curry =(fn) => {
if(typeof fn!=='function'){
throw Error('No function provided');
}
return function curriedFn(...args){
if(args.length < fn.length){
return function(){
return curriedFn.apply(null, args.concat( [].slice.call(arguments) ));
};
}
return fn.apply(null, args);
};
};
Listing 6-11curry Function Converting n arg Function to Unary Function
We have added the
part:
if(args.length < fn.length){
return function(){
return curriedFn.apply(null, args.concat( [].slice.call(arguments) ));
};
}
Let’s understand what’s happening in this piece of code, one element at a time.
This line checks if the argument that is passed via ...args length and the function argument list length is less or not. If so we go into the if block, or else we fall back to call the full function as before.
Once we enter the
if block, we use the
apply function to call
curriedFn recursively like this:
curriedFn.apply(null, args.concat( [].slice.call(arguments) ));
The snippet
args.concat( [].slice.call(arguments) )
is important. Using the
concat function
, we are concatenating the arguments that are passed one at a time and calling the
curriedFn recursively. Because we are combining all the passed arguments and calling it recursively, we will meet a point in which the line
if (args.length < fn.length)
condition fails. The argument list length (
args) and function argument length (
fn.length) will be equal, thus skipping the
if block and calling
return fn.apply(null, args);
which is going to yield the function’s full result!
With that understanding in place, we can use our
curry function to invoke the
multiply function:
curry(multiply)(3)(2)(1)
=> 6
Perfect! We have created our own curry function.
An important point to note is that our curry function is now converting a function of n arguments into a function that can be called as a unary function as the example shows.
Back to logger Function
Now let’s solve our
logger function
using the defined
curry function. Bringing up the function here for easy reference (Listing
6-8):
const loggerHelper = (mode,initialMessage,errorMessage,lineNo) => {
if(mode === "DEBUG")
console.debug(initialMessage,errorMessage + "at line: " + lineNo)
else if(mode === "ERROR")
console.error(initialMessage,errorMessage + "at line: " + lineNo)
else if(mode === "WARN")
console.warn(initialMessage,errorMessage + "at line: " + lineNo)
else
throw "Wrong mode"
}
The developer used to call the function:
loggerHelper("ERROR","Error At Stats.js","Invalid argument passed",23)
Now let’s solve the repeating first two
arguments problem via
curry:
let errorLogger = curry(loggerHelper)("ERROR")("Error At Stats.js");
let debugLogger = curry(loggerHelper)("DEBUG")("Debug At Stats.js");
let warnLogger = curry(loggerHelper)("WARN")("Warn At Stats.js");
Now we can easily refer to the earlier curried functions and use them under the respective context:
//for error
errorLogger("Error message",21)
=> Error At Stats.js Error messageat line: 21
//for debug
debugLogger("Debug message",233)
=> Debug At Stats.js Debug messageat line: 233
//for warn
warnLogger("Warn message",34)
=> Warn At Stats.js Warn messageat line: 34
That’s brilliant! We have seen how the curry function helps in the real world to remove a lot of boilerplates in function calls. Don’t forget to thank the closures concept, which is backing up the curry function. The debug module of the node uses the curry concept in its API (see
https://github.com/visionmedia/debug
).
Currying in Action
In the previous section we created our own curry function. We have also seen a simple example of using this curry function.
In this section we are going to see small but compact examples in which the currying technique is used. The examples shown in this section will help you better understand how to use currying in your day-to-day activities.
Finding a Number in Array Contents
Imagine we want to find the array
content that has a number. We can solve the problem via the following code snippet:
let match = curry(function(expr, str) {
return str.match(expr);
});
The returned
match function is a curried function. We can give the first argument
expr a regular expression
/[0-9]+/ that will indicate whether the content has a number in it.
let hasNumber = match(/[0-9]+/)
Now we will create a curried
filter function
:
let filter = curry(function(f, ary) {
return ary.filter(f);
});
With
hasNumber and
filter in place, we can create a new function called
findNumbersInArray:
let findNumbersInArray = filter(hasNumber)
Now you can test it:
findNumbersInArray(["js","number1"])
=> ["number1"]
Squaring an Array
We know how to square
contents of an array. We have also seen the same problem in previous chapters. We use the
map function and pass on the
square function to achieve the solution to our problem. Here we can use the
curry function to solve the same problem in another way:
let map = curry(function(f, ary) {
return ary.map(f);
});
let squareAll = map((x) => x * x)
squareAll([1,2,3])
=> [1,4,9]
As you can see in this example, we have created a new function, squareAll, that we can now use elsewhere in our code base. Similarly you can also do this for findEvenOfArray, findPrimeOfArray, and so on.
Data Flow
In both sections on using currying, we have designed the curried functions
such that they always take the array at the end. This is an intentional way of creating a curried function. As discussed in previous chapters, we as programmers often work on data structures like array, so making the array as the last argument allows us to create lot of reusable functions like squareAll and findNumbersInArray that we can use throughout the code base.
Partial Application
In this section we are going to see yet another function
called partial that allows developers to apply the function arguments partially.
Imagine we want to perform a set of operations every 10 milliseconds. Using the
setTimeout function
, we can do this:
setTimeout(() => console.log("Do X task"),10);
setTimeout(() => console.log("Do Y task"),10);
As you can see, we are passing on
10 for every one of our
setTimeout function calls. Can we hide that from the code? Can we use a
curry function to solve this problem? The answer is no, because the
curry function applies the argument from the leftmost to rightmost lists. Because we want to pass on the functions as needed and keep
10 as a constant (which is most of the argument list), we cannot use
curry as such. One workaround is that we can wrap our
setTimeout function
so that the function argument becomes the rightmost one:
const setTimeoutWrapper = (time,fn) => {
setTimeout(fn,time);
}
Then we can use our
curry function to wrap our
setTimeout to a
10-millisecond delay:
const delayTenMs = curry(setTimeoutWrapper)(10)
delayTenMs(() => console.log("Do X task"))
delayTenMs(() => console.log("Do Y task"))
which will work as we needed it to. The problem is, though, we must create wrappers like setTimeoutWrapper, which will be an overhead. That’s where we can use partial application techniques.
Implementing partial Function
To fully understand how the partial application technique is working, we will be creating our own partial function
in this section. Once the implementation is done, we will learn how to use our partial function with a simple example.
The implementation of the
partial function looks like Listing
6-12.
const partial = function (fn,...partialArgs){
let args = partialArgs;
return function(...fullArguments) {
let arg = 0;
for (let i = 0; i < args.length && arg < fullArguments.length; i++) {
if (args[i] === undefined) {
args[i] = fullArguments[arg++];
}
}
return fn.apply(null, args);
};
};
Listing 6-12partial Function Definition
Let’s quickly use the
partial function with our current problem:
let delayTenMs = partial(setTimeout,undefined,10);
delayTenMs(() => console.log("Do Y task"))
which will print to the console as you expect. Now let’s walk through the
implementation
details of the
partial function. Using closures, we are capturing the arguments that are passed to the function for the first time:
partial(setTimeout,undefined,10)
//will lead to
let args = partialArgs
=> args = [undefined,10]
We return a function that will remember the
args value (yes, we are using closures again). The returned function is very easy. It takes an argument called
fullArguments, so we call functions like
delayTenMs by passing this argument:
delayTenMs(() => console.log("Do Y task"))
//fullArguments points to
//[() => console.log("Do Y task")]
//args using closures will have
//args = [undefined,10]
Now in the
for loop we iterate and create the necessary arguments array for our function:
if (args[i] === undefined) {
args[i] = fullArguments[arg++];
}
}
Now let’s start with value
i as
0:
//args = [undefined,10]
//fullArguments = [() => console.log("Do Y task")]
args[0] => undefined === undefined //true
//inside if loop
args[0] = fullArguments[0]
=> args[0] = () => console.log("Do Y task")
//thus args will become
=> [() => console.log("Do Y task"),10]
As you can see in those code snippet examples
, our args point to the array as we would expect for setTimeout function calls. Once we have the necessary arguments in args, we call the function via fn.apply(null, args).
Remember that we can apply
partial for any function that has
n arguments. To make the point concrete, let’s look at an example. In JavaScript we use the following function call to do JSON pretty print:
let obj = {foo: "bar", bar: "foo"}
JSON.stringify(obj, null, 2);
As you can see, the last two arguments for the
function
called
stringify are always going to be the same:
null,
2. We can use
partial to remove the boilerplate:
let prettyPrintJson = partial(JSON.stringify,undefined,null,2)
You can then use
prettyPrintJson to print the JSON:
prettyPrintJson({foo: "bar", bar: "foo"})
which will give you this output:
"{
"foo": "bar",
"bar": "foo"
}"
Currying vs. Partial Application
We have seen both techniques
, so the question is when to use which one. The answer depends on how your API is defined. If your API is defined as map,filter, then we can easily use the curry function to solve our problem. As discussed in the previous section, though, life is not always easy. There could be functions that are not designed for curry such as setTimeout in our examples. In those cases, the best option would be to use partial functions. After all, we use curry or partial to make function arguments and function setup easy and more powerful.
It’s also important to note that currying will return nested unary functions; we have implemented curry so that it takes n arguments
just for our convenience. It’s also a proven fact that developers need either curry or partial but not both.
Summary
Currying and partial application are always a tool in functional programming. We started the chapter by explaining the definition of currying, which is nothing but converting a function of n arguments into nested unary functions. We saw the examples of currying and where it can be very useful, but there are cases where you want to fill the first two arguments of a function and the last argument, leaving the middle argument unknown for a certain time. That’s where partial application comes into the picture. To fully understand both these concepts, we have implemented our own curry and partial functions. We have made a lot of progress, but we’re not done yet.
Functional programming is all about composing functions, namely composing several small functions to build a new function. Composing and pipelines are the topics of the next chapter.