© Anto Aravinth, Srikanth Machiraju 2018
Anto Aravinth and Srikanth MachirajuBeginning Functional JavaScripthttps://doi.org/10.1007/978-1-4842-4087-8_6

6. Currying and Partial Application

Anto Aravinth1  and Srikanth Machiraju2
(1)
Chennai, Tamil Nadu, India
(2)
Hyderabad, Andhra Pradesh, India
 

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.

Note

The chapter examples and library source code are in branch chap06. The repo’s URL is https://github.com/antoaravinth/functional-es8.git .

Once you check out the code, please check out branch chap06:

...

git checkout -b chap06 origin/chap06

...

For running the codes, as before run:

...

npm run playground

...

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-1

Unary 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-2

Binary 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-3

Variadic Function

We call the variadic function like this:
variadic(1,2,3)
=> 1
=> [1,2,3]

Note

As you can see in the output, arguments do capture all the arguments that are passed to a function.

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-4

Variadic 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:
addCurried(4)
it returns a function where x value is captured via the closure concept as we saw in earlier chapters:
=> fn = y => 4 + y
We can call the addCurried function like this to get the proper result:
addCurried(4)(4)
=> 8
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-5

curry Function Definition

Note

We have written the curry function in ES5 format so that we can visualize the process of returning a nested unary function.

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-6

tables 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-7

tables 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-8

Simple 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-9

Revisting 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-10

curry 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:
multiply(1,2,3)

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-11

curry 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.
args.length < fn.length

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.

Note

You can call the preceding code snippet like the following, too:

let curriedMul3 = curry(multiply)(3)

let curriedMul2 = curriedMul3(2)

let curriedMul1 = curriedMul2(1)

where curriedMul1 will be equal to 6. We use curry(multiply)(3)(2)(1), though, as it is much more readable.

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.

Note

In our source code companion, we have called the curry function curryN. It’s just to keep the old curry as is, which was supposed to do currying on binary functions.

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-12

partial 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"
}"

Note

There is a slight bug in our implementation of the partial function. What if you call prettyPrintJson again with a different argument? Does it work?

It always gives the result for the first invoked argument, but why? Can you see where we are making the mistake?

Hint: Remember, we are modifying the partialArgs by replacing the undefined values with our argument, and Arrays are used for reference.

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.