Functional JavaScript by Jesse Farmer is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
Contact Jesse at [email protected] if you have any questions!
Functional JavaScript by Jesse Farmer is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
Contact Jesse at [email protected] if you have any questions!
| var isEven = function(n) { return n % 2 === 0; }, | |
| isOdd = not(isEven); | |
| var add = curry(function(a,b) { return a + b; }), | |
| inc = add(1), | |
| incAll = map(inc); | |
| var times = curry(function(a,b) { return a * b; }), | |
| double = times(2), | |
| doubleAll = map(double); | |
| console.log("" + cons(cons(1,2), cons(3,4)), "((1 2) (3 4))"); | |
| console.log("" + incAll(list(1,2,3)), "(2 (3 (4 null)))"); | |
| var evens = list(0,2,4,6,8), | |
| odds = list(1,3,5,7,9); | |
| var areAnyEven = any(isEven), | |
| areAnyOdd = any(isOdd), | |
| areAllOdd = all(isOdd); | |
| console.log("" + areAnyOdd(odds), "true"); | |
| console.log("" + areAnyOdd(evens), "false"); | |
| console.log("" + areAllOdd(odds), "true"); | |
| console.log("" + areAllOdd(evens), "false"); | |
| console.log("" + areAnyOdd(doubleAll(odds)), "false"); | |
| console.log("" + areAnyEven(incAll(evens)), "false"); | |
| console.log("" + areAllOdd(incAll(evens)), "true"); | |
| var onlyEvens = filter(isEven), | |
| numbers = list(1,2,3,4); | |
| console.log("" + onlyEvens(numbers), "(2 (4 null))"); |
| var argsToArray = Function.prototype.call.bind(Array.prototype.slice); | |
| var curry = function(fn, filled) { | |
| var filled = filled || []; | |
| var arity = fn.length; | |
| return function() { | |
| var args = argsToArray(arguments); | |
| var newFilled = filled.concat(args); | |
| if (newFilled.length >= arity) { | |
| return fn.apply(this, newFilled); | |
| } else { | |
| return curry(fn, newFilled); | |
| } | |
| } | |
| }; | |
| // A helper method to call | |
| var toString = function(o) { | |
| if (o !== null && typeof(o) !== 'object' && typeof(o) !== 'function') { | |
| return o.toString(); | |
| } else { | |
| return "" + o; | |
| } | |
| }; | |
| var pairify = function(fn) { | |
| fn.pair = true; | |
| fn.toString = function() { | |
| if (isEmpty(this)) { return "()"; } | |
| var head = first(this), | |
| tail = rest(this); | |
| // FIXME: This displays cons(1, cons(2, 3)) as (1 (2 3)) vs. (1 2 3) | |
| return "(" + toString(head) + " " + toString(tail) + ")"; | |
| }; | |
| return fn; | |
| }; | |
| var cons = curry(function(x,y) { | |
| return pairify(function(fn) { return fn(x, y); }); | |
| }); | |
| var first = function(fn) { | |
| return fn(function(x, y) { return x }); | |
| }; | |
| var rest = function(fn) { | |
| return fn(function(x, y) { return y }); | |
| }; | |
| // e.g., list(1,2,3,4) == cons(1, cons(2, cons(3, cons(4, null)))) | |
| var list = function() { | |
| var args = argsToArray(arguments); | |
| return (args.length === 0) ? null : cons(args.shift(), list.apply(null, args)); | |
| }; | |
| var isAtom = function(x) { | |
| return (x !== null) && (x !== undefined) && !x.pair; | |
| }; | |
| var isPair = function(x) { | |
| return x && x.pair; | |
| }; | |
| var isNull = function(x) { | |
| return x === null; | |
| } | |
| var isZero = function(x) { | |
| return x === 0; | |
| } | |
| var isEmpty = function(lst) { | |
| return isNull(lst) || isNull(first(lst)); | |
| }; | |
| var foldl = curry(function(fn, acc, lst) { | |
| return isEmpty(lst) ? acc : foldl(fn, fn(acc, first(lst)), rest(lst)); | |
| }); | |
| var foldr = curry(function(fn, acc, lst) { | |
| return isEmpty(lst) ? acc : fn(first(lst), foldr(fn, acc, rest(lst))); | |
| }); | |
| var map = curry(function(fn, lst) { | |
| return foldr(function(acc, lst) { return cons(fn(acc), lst) }, null, lst); | |
| }); | |
| var filter = curry(function(fn, lst) { | |
| return foldr(function(acc, lst) { return fn(acc) ? cons(acc, lst) : lst }, null, lst); | |
| }); | |
| var append = curry(function(to, lst) { | |
| return isEmpty(to) ? lst : cons(first(to), append(rest(to), lst)); | |
| }); | |
| var reverse = function(lst) { | |
| return isEmpty(lst) ? lst : append(reverse(rest(lst)), list(first(lst))); | |
| }; | |
| var not = curry(function(pred, arg) { | |
| return !pred(arg) | |
| }); | |
| var any = curry(function(pred, lst) { | |
| return isEmpty(lst) ? false : (pred(first(lst)) || any(pred, rest(lst))); | |
| }); | |
| var all = curry(function(pred, lst) { | |
| return !any(not(pred), lst); | |
| }); | |
| var flip = function(fn) { | |
| return curry(function(a,b) { return fn(b,a) }); | |
| }; |