Last active
December 25, 2019 04:04
-
-
Save zeusdeux/5b8ade23e9f551ae03f7 to your computer and use it in GitHub Desktop.
Functional programming toolkit for javascript
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
//this function composes functions like in Haskell (and most other functional languages) | |
//the final composed function can accept parameters dictated by the chain it creates | |
//you can pass it a list of functions and it shall apply them from RIGHT to LEFT (right associativeness?) | |
//eg., c0(fn1, fn2, fn3)(10) => return fn1(fn2(fn3(10))); | |
// out<---<----<----<=10 | |
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)); | |
} | |
//simple example: | |
function add3(x) { | |
return x + 3; | |
} | |
function mult10(x) { | |
return x * 10; | |
} | |
function negate(x) { | |
return -x; | |
} | |
chain = c0(negate, mult10, add3); | |
//chain is now a reusable function composed of 3 other functions | |
//let's use it then | |
chain(10); //negate(mult10(add3(10))) -> -130 | |
chain(20); //-230 | |
//c0 and cu (curry.js, below) are meant to be used together to get powerful function chains |
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
//this function returns a function that curries itself if it doesn't have enough parameters | |
//when it has enough parameters, it runs the function and gives you the result | |
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); | |
} | |
} | |
//simple example: | |
//add a,b and c | |
function add(a, b, c) { | |
return a + b + c; | |
} | |
var add = cu(add); | |
//curry it with a = 1 | |
var add1 = add(1); | |
//curry it so that a=1 and b=2 | |
var add1plus2 = add1(2); | |
console.log(add(1, 2, 5) === add1(2, 5)); //true | |
console.log(add(1, 2, 5) === add1plus2(5)); //true |
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
//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"] | |
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
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); | |
} | |
}); | |
//accepts function that take two parameter and flips its params | |
var flip = function(fn){ | |
return cu(function(a,b){ | |
return fn.call(this, b, a); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment