libs: - lodash --- # Currying [Currying](https://en.wikipedia.org/wiki/Currying) is an advanced technique of working with functions. It's used not only in JavaScript, but in other languages as well. Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`. Currying doesn't call a function. It just transforms it. Let's see an example first, to better understand what we're talking about, and then practical applications. We'll create a helper function `curry(f)` that performs currying for a two-argument `f`. In other words, `curry(f)` for two-argument `f(a, b)` translates it into a function that runs as `f(a)(b)`: ```js run *!* function curry(f) { // curry(f) does the currying transform return function(a) { return function(b) { return f(a, b); }; }; } */!* // usage function sum(a, b) { return a + b; } let curriedSum = curry(sum); alert( curriedSum(1)(2) ); // 3 ``` As you can see, the implementation is straightforward: it's just two wrappers. - The result of `curry(func)` is a wrapper `function(a)`. - When it is called like `sum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`. - Then this wrapper is called with `2` as an argument, and it passes the call to the original `sum`. More advanced implementations of currying, such as [_.curry](https://lodash.com/docs#curry) from lodash library, return a wrapper that allows a function to be called both normally and partially: ```js run function sum(a, b) { return a + b; } let curriedSum = _.curry(sum); // using _.curry from lodash library alert( curriedSum(1, 2) ); // 3, still callable normally alert( curriedSum(1)(2) ); // 3, called partially ``` ## 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`: ```js function log(date, importance, message) { alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); } ``` Let's curry it! ```js log = _.curry(log); ``` After that `log` works normally: ```js log(new Date(), "DEBUG", "some debug"); // log(a, b, c) ``` ...But also works in the curried form: ```js log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) ``` Now we can easily make a convenience function for current logs: ```js // 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 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: ```js 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. ## Advanced curry implementation In case you'd like to get in details, here's the "advanced" curry implementation for multi-argument functions that we could use above. It's pretty short: ```js function curry(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this, args); } else { return function(...args2) { return curried.apply(this, args.concat(args2)); } } }; } ``` Usage examples: ```js function sum(a, b, c) { return a + b + c; } let curriedSum = curry(sum); alert( curriedSum(1, 2, 3) ); // 6, still callable normally alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg alert( curriedSum(1)(2)(3) ); // 6, full currying ``` The new `curry` may look complicated, but it's actually easy to understand. The result of `curry(func)` call is the wrapper `curried` that looks like this: ```js // func is the function to transform function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); } else { return function pass(...args2) { // (2) return curried.apply(this, args.concat(args2)); } } }; ``` When we run it, there are two `if` execution branches: 1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it. 2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result. For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`. For the call `curried(1)(2)(3)`: 1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`. 2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together. As the argument count is still less than 3, `curry` returns `pass`. 3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function. If that's still not obvious, just trace the calls sequence in your mind or on paper. ```smart header="Fixed-length functions only" The currying requires the function to have a fixed number of arguments. A function that uses rest parameters, such as `f(...args)`, can't be curried this way. ``` ```smart header="A little more than currying" By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`. But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant. ``` ## Summary *Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough. Currying allows to easily get partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.