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) }); | |
}; |