//function composition goodness function c0(f, g) { var last; if (!arguments.length) return; if (arguments.length === 1) return f; if (arguments.length === 2) { return function (x) { var args = [].slice.call(arguments); args.shift(); args.push(g(x)); return f.apply(this, args); }; } last = [].pop.call(arguments); return c0.apply(null, ([].push.call(arguments, c0([].pop.call(arguments), last)), arguments)); } //automagical self-currying function goodness function cu(fn) { var args = [].slice.call(arguments); if (args.length - 1 >= fn.length) return fn.apply(this, args.slice(1)); return function () { var tempArgs = args.concat([].slice.call(arguments)); return cu.apply(this, tempArgs); } } /******************************************************************** * Example 1: Single argument to composed function * ----------------------------------------------- *********************************************************************/ //filters out a particular item (given by 'x') from the array function filterOut(x, arr) { return arr.filter(function (v) { if (x !== v) return v; }) } //multiples each item in the array with the value 'x' function multiplyWith(x, arr) { return arr.map(function (v) { return x * v; }); } //make a self-currying version of [].reduce reduce = cu([].reduce); //display the array and then sum up all its items and return it function showAndReduce(arr) { console.log(arr); return reduce.call(arr, function (p, c) { return p + c }, 0); } //test input var someArray = [100, 1, 2, 100, 100, 3, 100, 4, 100]; //create a chain that filters out all 100's, //multiplies each item with 10 and then shows and returns the sum var chainOne = c0(showAndReduce, cu(multiplyWith, 10), cu(filterOut, 100)); chainOne(someArray); //output: //[10, 20, 30, 40] //100 chainOne([100, 100, 100, 100, 100, 1, 1, 1, 1, 2, 100]); //output: //[10, 10, 10, 10, 20] //60 /******************************************************************** * Example 2: multiple args to a composed function * ----------------------------------------------- *********************************************************************/ //We shall be using map, filter and with a few helpers //map, filter, add & lessThan all auto-curry. Flip need not auto-curry but its result does var map = cu(function map(list, fn) { try { return list.map(fn); } catch (e) { return [].map.call(list, fn); } }); var filter = cu(function filter(list, fn) { try { return list.filter(fn); } catch (e) { return [].filter.call(list, fn); } }); var add = cu(function(a, b){ return a+b; }); var lessThan = cu(function(a,b){ return b<a; }); //the function reads "lessThan 'a' is 'b'?" or "is 'b' lessThan 'a'" var flip = function(fn){ return cu(function(a,b){ return fn.call(this, b, a); }); }; //add 1 to every element in the array and return the result c0(map, add)(1, [1,2,3,4]); //[2, 3, 4, 5] //give me every element that is lesser than 4 in the array c0(filter, lessThan)(4, [0,1,2,3,4]); //[0, 1, 2, 3] //string fun c0(map, add)(" ola", "abcd"); //[" olaa", " olab", " olac", " olad"] //but wait, that's not what we wanted. We wanted ["a ola", "b ola", ...] c0(map, flip(add))(" ola", "abcd"); //["a ola", "b ola", "c ola", "d ola"]