Created
October 30, 2017 21:04
-
-
Save andrewb/50e8522d6c2c5ef9cbee37b7ecc4656b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import curry from 'lodash.curry'; | |
/* | |
CURRYING | |
What is a curried function? | |
It's a function that takes multiple parameters, | |
one at a time. | |
What does that mean? | |
Well, imagine a function that has three parameters, | |
like: | |
function foo(a, b, c) { | |
... | |
} | |
The curried version will take one argument and return a | |
function that takes the next argument. This returned function | |
will also return a function that takes the third and final | |
argument. Calling this function will return the result of the | |
function. | |
What? | |
foo(1, 2, 3); // a flavorless function | |
foo(1)(2)(3); // a tasy, curried function | |
Let's make this a little more concrete. Image we want a curried | |
function that adds two numbers together. How could we implement | |
it in a naive way? | |
const addAB = (a) => { | |
return (b) => { | |
return a + b; | |
}; | |
} | |
Or, more simply... | |
const addAB = a => b => a + b; | |
*/ | |
const addAB = a => b => a + b; | |
console.log( addAB(1)(2) ); // 3 | |
const add10 = addAB(10); | |
console.log( add10(10) ); // 20 | |
/* | |
The above example is contrived and impractical. We'll look at | |
real life applications of currying soon. For now, the main | |
thing is that the concept makes sense. Before I go on is it | |
clear to everyone? | |
PARTIAL APPLICATION | |
We know that a curried function will return a function that takes | |
exactly one argument. | |
Partial application is similar, but it will return a function that | |
takes the remaining arguments. | |
Huh? | |
To demonstrate this lets imagine we have a new function that adds | |
A, B and C. | |
If we curried this function we would use it like this: | |
addABC(1)(2)(3); // 6 | |
But, if we partially applied the function we could use it like this... | |
*/ | |
const addABC = (a, b, c) => { | |
return a + b + c; | |
} | |
// To keep it simple, let's just use bind for this demonstration... | |
const addBC = addABC.bind(null, 1); | |
// Our new function already has A applied | |
console.log( addBC(2, 3) ); // 6 | |
const addC = addABC.bind(null, 1, 2); | |
// Our new function already has A and B applied | |
console.log( addC(3) ); // 6 | |
/* | |
The distinction is important. A curried function will always produce | |
unary functions. The same is not always true of partial application. | |
As we saw above it can return unary functions (addC), but it return | |
functions that take multiple arguments too. | |
The partial application example above is a little less tedious than | |
our curried example. But IRL we would want something more flexible. You | |
could write your own functions to implement curring and partial application | |
or you could use something like lodash. | |
A simple implementation would be: | |
(binary) | |
const curry2 = f => x => y => f(x, y); // curry2(addAB) | |
(ternary) | |
const curry3 = f => x => y => z => f(x, y, z); // curry3(addABC) | |
Or, for more flexibility use lodash. Note, the lodash curry function | |
is not true currying. It is best described as auto-currying. It | |
basically lets us curry, or partially apply in any order. | |
*/ | |
// Lodash (auto)-currying | |
const curried = curry(addABC); | |
console.log( curried(1, 2, 3) ); // 6 (call it) | |
console.log( curried(1, 2)(3) ); // 6 (partial) | |
console.log( curried(1)(2, 3) ); // 6 (partial) | |
console.log( curried(1)(2)(3) ); // 6 (curry) | |
/* | |
Great, so we can curry or partially apply any function easily using lodash | |
or another lib, but when why would I want to do it? | |
Getting a little less abstract curry works well with functional composition | |
because it returns unary functions. | |
*/ | |
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); | |
const add = (a, b) => a + b; | |
const multiply = (a, b) => a * b; | |
const square = x => x * x; | |
// (25 + 10) * 10 | |
console.log( pipe(square, curry(add)(10), curry(multiply)(10) )(5) ); // 350 | |
/* | |
Ok, that's still pretty abstract, but if you want to do functional | |
programming you'll use it frequently. | |
Even if you're not doing functional programming currying can still be useful. | |
Imagine this code... | |
*/ | |
const sanitize = (blacklist, string) => { | |
// imagine this returns a new string with all | |
// of the blacklisted values removed. | |
// e.g. sanitize(['foo', 'bar'], 'foo bar baz') | |
// returns 'baz' | |
} | |
const strings = [ | |
'My great string', | |
'Another great string' | |
]; | |
// Without curry | |
const blacklist = ['foo', 'bar']; | |
const sanitized1 = strings.map((str) => sanitize(blacklist, str)); | |
// Or, with... | |
const clean = curry(sanitize)(['foo', 'bar']); // returns unary function | |
const sanitized2 = strings.map(clean); | |
/* | |
Partial application is a nice way to make handy functions from generic | |
ones. Imagine a generic converter. | |
*/ | |
const converter = (unit, factor, input) => { | |
return `${(input * factor).toFixed(2)} ${unit}`; | |
} | |
// Lodash | |
const milesToKilometers = curry(converter)('km', 1.60936); | |
// Bind | |
const poundsToKilograms = converter.bind(null, 'kg', 0.45460); | |
console.log( milesToKilometers(5) ); | |
console.log( poundsToKilograms(2) ); | |
/* | |
That's it. In the near future we'll talk a bit about point-free style, | |
touch on pure functions (I don't think we need to go in depth here...) | |
and look at functional programming. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment