Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
180 lines (150 sloc) 4.83 KB
<!doctype html>
<html lang="en">
<head>
<title>JavaScript Patterns</title>
<meta charset="utf-8">
</head>
<body>
<script>
/* Title: Currying
Description: used to create new functions dynamically by partially applying a set of arguments
in ECMAScript 5, you can use bind to implement curring.
*/
/***
function application
***/
// define a function
var sayHi = function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
};
// invoke a function
sayHi(); // "Hello!"
sayHi('world'); // "Hello, world!"
// apply a function
sayHi.apply(null, ["hello"]); // "Hello, hello!"
var alien = {
sayHi:function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
}
};
alien.sayHi('world'); // "Hello, world!"
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
// the second is more efficient, saves an array
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
sayHi.call(alien, "humans"); // "Hello, humans!"
/***
partial application
***/
// for illustration purposes
// not valid JavaScript
// we have this function
function add(x, y) {
return x + y;
}
// and we know the arguments
add(5, 4);
// step 1 -- substitute one argument
function add(5, y) {
return 5 + y;
}
// step 2 -- substitute the other argument
function add(5, 4) {
return 5 + 4;
}
/***
currying
***/
// a curried add()
// accepts partial list of arguments
function add(x, y) {
var oldx = x, oldy = y;
if (typeof oldy === "undefined") { // partial
return function (newy) {
return oldx + newy;
}
}
// full application
return x + y;
}
// test
typeof add(5); // "function"
add(3)(4); // 7
// create and store a new function
var add2000 = add(2000);
add2000(10); // 2010
function schonfinkelize(fn) {
var slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function () {
var new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
};
}
// reference
// http://www.jspatterns.com/
// http://shop.oreilly.com/product/9780596806767.do?sortby=publicationDate
// Here's a generic curry function (from kybernetikos):
// It's slightly more complex than the common examples of currying (e.g. the one above), but that's because
// the function can afterwards be called either in the normal way, or in the curried way.
// If you call a 3 argument curried function with it, the returned function *is also curried*.
// e.g. you can write func(arg1)(arg2)(arg3) or func(arg1, arg2, arg3) or func(arg1)(arg2, arg3) etc...
/* the curry procedure needs to know how many arguments are required before it should calculate the result */
function curry(func, minArgs) {
if (minArgs == undefined) {
minArgs = 1;
}
function funcWithArgsFrozen(frozenargs) {
return function () {
// could do an optimisation here - if called with no arguments
// return exactly this function.
var args = Array.prototype.slice.call(arguments);
var newArgs = frozenargs.concat(args);
if (newArgs.length >= minArgs) {
return func.apply(this, newArgs);
} else {
return funcWithArgsFrozen(newArgs);
}
};
}
return funcWithArgsFrozen([]);
}
/* Here are some example uses - I use these with a functional immutable list structure implemented in js
but they also work ok for normal higher order functions over arrays.
*/
var plus = curry(function () {
var result = 0;
for (var i = 0; i < arguments.length; ++i) {
result += arguments[i];
}
return result;
}, 2);
/* Now you can call
plus(3,2) // normal call
plus(3) // partial application (returns a function that adds 3 to its argument)
plus(3)(2) // complete application (returns 5)
plus()(3)()()(2) // returns 5
plus(3, 2, 4, 5) // the normal call can optionally take more than the minimum number of arguments
plus(3)(2, 3, 5) // the last application can too.
*/
var minus = curry(function (x) {
var result = x;
for (var i = 1; i < arguments.length; ++i) {
result -= arguments[i];
}
return result;
}, 2);
/* flip switches the order of the first two arguments on a function. It is curried itself and
the function it returns is curried too. Particularly useful if you want a function that subtracts a number */
var flip = curry(function (func) {
return curry(function (a, b) {
return func(b, a);
}, 2);
});
/* for example
minus(5) // returns a function that takes its argument away from 5
flip(minus)(5) // returns a function that takes 5 away from its argument
*/
</script>
</body>
</html>