Last active
March 1, 2017 09:03
-
-
Save YozhEzhi/876c4d05ba3895cd0a6d4db8ea248f39 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
* @author https://github.com/vasanthk/js-bits/blob/master/js/currying.js | |
* Currying refers to the process of transforming a function with multiple arity (# or args a fn accepts) | |
* into the same function with less arity. | |
* | |
* Briefly, currying is a way of constructing functions that allows partial application of a function’s arguments. | |
* What this means is that you can pass all of the arguments a function is expecting and get the result, | |
* or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments. | |
* | |
* Currying vs Partial Application | |
* “Currying is the decomposition of a polyadic function into a chain of nested unary functions. | |
* Thus decomposed, you can partially apply one or more arguments, although the curry operation itself does not | |
* apply any arguments to the function.” | |
*/ | |
// Simple Greet function -- Non Curried | |
const greet = function (greeting, name) { | |
console.log(`${greeting}, ${name}`); | |
}; | |
greet('Hello', 'Vasa'); // 'Hello, Vasa' | |
// Curried version | |
const greetCurried = function (greeting) { | |
return function (name) { | |
console.log(`${greeting}, ${name}`); | |
} | |
}; | |
// This tiny adjustment lets us create a new function for any type of greeting, | |
// and pass that new function the name of the person that we want to greet: | |
const greetHello = greetCurried('Hello'); | |
greetHello('Vasa'); // 'Hello, Vasa' | |
greetHello('Vignesh'); // 'Hello, Vignesh' | |
// We can also call the original curried function directly by passing each of the params in a separate set of parentheses: | |
greetCurried('Hi there')('Vasa'); // 'Hi there, Vasa' | |
// Currying traditional functions -- SIMPLE VERSION | |
// The only problem with the currying approach is the syntax. As you build these curried functions up, you need to keep | |
// nesting returned functions, and call them with new functions that require multiple sets of parentheses, | |
// each containing its own isolated argument. It can get messy. | |
// To address that problem, one approach is to create a quick and dirty currying function that will take the name of an | |
// existing function that was written without all the nested returns. | |
// A currying function would need to pull out the list of arguments for that function, and use those to return a curried | |
// version of the original function: | |
// Partial Application -- Few arguments passed initially + allowing more args to be passed later on. | |
function curryIt(uncurriedFn) { | |
// Omit 0th argument (which is the uncurriedFn and start from index 1) | |
var parameters = Array.prototype.slice.call(arguments, 1); | |
return function () { | |
return uncurriedFn.apply(this, parameters.concat( | |
Array.prototype.slice.call(arguments, 0) | |
)); | |
}; | |
} | |
var greeter = function (greeting, separator, emphasis, name) { | |
console.log(greeting + separator + name + emphasis); | |
}; | |
var greetHello = curryIt(greeter, 'Hello', ', ', '.'); | |
greetHello('Heidi'); // 'Hello, Heidi.' | |
greetHello('Eddie'); // 'Hello, Eddie.' | |
// curryIt --- BETTER (?) VERSION | |
// Reference: https://medium.com/@kevincennis/currying-in-javascript-c66080543528#.bnk4cy1m0 | |
function curryIt(fn) { | |
var arity = fn.length; | |
return (function resolver() { | |
var memory = Array.prototype.slice.call(arguments); | |
return function () { | |
var local = memory.slice(), next; | |
Array.prototype.push.apply(local, arguments); | |
next = local.length >= arity ? fn : resolver; | |
return next.apply(null, local); | |
}; | |
}()); | |
} | |
var l = 2, b = 3, h = 4; | |
var curriedVol = curryIt(vol); | |
var area = curriedVol(l)(b); | |
var volume = area(h); | |
console.log('Volume: ', volume); | |
function vol(l, b, h) { | |
return l * b * h; | |
} | |
// ES6 Example | |
const one = document.getElementById('one'); | |
const two = document.getElementById('two'); | |
const three = document.getElementById('three'); | |
const f = a => b => c => a.addEventListener(b, (event) => event.target.style.color = c); | |
const oneEventColor = f(one); | |
const twoEventColor = f(two); | |
oneEventColor('mouseover')('blue'); | |
twoEventColor('mouseout')('green'); | |
// Sum numbers example: | |
function add(...args) { | |
return (args.length > 0) ? args.reduce((a, b) => a + b) : 0; | |
} | |
function curry(func) { | |
return function(...nextArgs) { | |
// run function | |
if (nextArgs.length === 0) return func.apply(this, [...nextArgs]); | |
// else return a partially applied function | |
return curry(func.bind.apply(func, [this, ...nextArgs])); | |
}; | |
} | |
const sum = curry(add); | |
console.log(sum(), 0); | |
console.log(sum(3)(2)(5)(8)(3)(), 21); | |
const sum10 = sum(2)(4)(4); // ==> Function | |
console.log(sum10(), 10); // ==> 10 | |
const sum13 = sum10(3); // ==> Function | |
console.log(sum13(), 13); // ==> 13 | |
console.log(sum13(-4)(), 9); // ==> 9 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment