Sunday, March 29, 2020

Currying in JavaScript

Currying is a fundamental tool in functional programming, a programming pattern that tries to minimize the number of changes to a program’s state (known as side effects) by using immutable data and pure (no side effects) functions.

Currying


Currying is the process of taking a function with multiple arguments and returning a series of functions that take one argument and eventually resolve to a value.
Here’s a simple example to illustrate it using the _.curry function from Lodash (we’ll build our own curry function shortly):
function volume(l, w, h) {
  return l * w * h;
}

const curried = _.curry(volume);

volume(2, 3, 4); // 24
curried(2)(3)(4); // 24
The original function volume takes three arguments, but once curried we can instead pass in each argument to three nested functions.
In other words, currying has effectively done this:
function volume1(length) {
  return function(width) {
    return function(height) {
      return height * width * length;
    }
  }
}

volume1(2)(3)(4); // 24
If you’re confused how the innermost function has access to the variables in outer functions, learn more about closures and scoping in JavaScript.

Currying? What for?

To understand the benefits we need a worthy real-life example.
For instance, we have the logging function log(date, importance, message) that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we’ll just use alert:
function log(date, importance, message) {
  alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}
Let’s curry it!
log = _.curry(log);
After that log works normally:
log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
…But also works in the curried form:
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
Now we can easily make a convenience function for current logs:
// logNow will be the partial of log with fixed first argument
let logNow = log(new Date());

// use it
logNow("INFO", "message"); // [HH:mm] INFO message
Now logNow is log with the fixed first argument, in other words, “partially applied function” or “partial” for short.
We can go further and make a convenience function for current debug logs:
let debugNow = logNow("DEBUG");

debugNow("message"); // [HH:mm] DEBUG message
So:
  1. We didn’t lose anything after currying: log is still callable normally.
  2. We can easily generate partial functions such as for today’s logs.

function declaration, expression and call/invoke/execution

  The  function  declaration defines a function with the specified parameters. The  function   keyword can be used to define a function ins...