Книга: Functional Programming in JavaScript
Назад: Partial application
Дальше: Function composition

Currying

Currying is the process of transforming a function with many arguments into a function with one argument that returns another function that takes more arguments as needed. Formally, a function with N arguments can be transformed into a function chain of N functions, each with only one argument.

A common question is: what is the difference between partial application and currying? While it's true that partial application returns a value right away and currying only returns another curried function that takes the next argument, the fundamental difference is that currying allows for much better control of how arguments are passed to the function. We'll see just how that's true, but first we need to create function to perform the currying.

Here's our polyfill for adding currying to the Function prototype:

Function.prototype.curry = function (numArgs) {   var func = this;   numArgs = numArgs || func.length;    // recursively acquire the arguments   function subCurry(prev) {     return function (arg) {       var args = prev.concat(arg);       if (args.length < numArgs) {         // recursive case: we still need more args         return subCurry(args);       }       else {         // base case: apply the function         return func.apply(this, args);       }     };   }   return subCurry([]); };

The argument lets us optionally specify the number of arguments the function being curried needs if it's not explicitly defined.

Let's look at how to use it within our hexadecimal application. We'll write a function that converts RGB values to a hexadecimal string that is appropriate for HTML:

function rgb2hex(r, g, b) {   // nums2hex is previously defined in this chapter   return '#' + nums2hex(r) + nums2hex(g) + nums2hex(b); } var hexColors = rgb2hex.curry(); console.log(hexColors(11)) // returns a curried function console.log(hexColors(11,12,123)) // returns a curried function console.log(hexColors(11)(12)(123)) // returns #0b0c7b console.log(hexColors(210)(12)(0))  // returns #d20c00

It will return the curried function until all needed arguments are passed in. And they're passed in the same left-to-right order as defined by the function being curried.

But we can step it up a notch and define the more specific functions that we need as follows:

var reds = function(g,b){return hexColors(255)(g)(b)}; var greens = function(r,b){return hexColors(r)(255)(b)}; var blues  = function(r,g){return hexColors(r)(g)(255)}; console.log(reds(11, 12))   // returns #ff0b0c console.log(greens(11, 12)) // returns #0bff0c console.log(blues(11, 12))  // returns #0b0cff

So that's a nice way to use currying. But if we just want to curry our function directly, we run into a little bit of trouble. And that's because the function doesn't define any arguments, it just lets you pass as many arguments in as you want. So we have to define the number of arguments. We do that with the optional parameter to the curry function that allows us to set the number of arguments of the function being curried.

var hexs = nums2hex.curry(2); console.log(hexs(11)(12));     // returns 0b0c console.log(hexs(11));         // returns function console.log(hexs(110)(12)(0)); // incorrect

Therefore currying does not work well with functions that accept variable numbers of arguments. For something like that, partial application is preferred.

All of this isn't just for the benefit of function factories and code reuse. Currying and partial application play into a bigger pattern known as composition.

Назад: Partial application
Дальше: Function composition

bsn
thank
Vesa Karvonen
I hope you don't mind, but I’d like to point you and your readers to my high-performance optics library for JavaScript that is in production use in multiple projects, has comprehensive support for partial optics and interactive documentation: https://calmm-js.github.io/partial.lenses/ (this takes a moment to load — be patient!)