简介
附录介绍了使用JavaScript进行函数式编程的常用功能:
- Array Functions:
var flatten = function(arrays) {return arrays.reduce(function(p, n) {return p.concat(n);});};var invert = function(arr) {return arr.map(function(x, i, a) {return a[a.length - (i + 1)];});};
- Binding Functions:
var bind = Function.prototype.call.bind(Function.prototype.bind);var call = bind(Function.prototype.call, Function.prototype.call);var apply = bind(Function.prototype.call, Function.prototype.apply);
- Category Theory:
var checkTypes = function(typeSafeties) {arrayOf(func)(arr(typeSafeties));var argLength = typeSafeties.length;return function(args) {arr(args);if (args.length != argLength) {throw new TypeError("Expected " + argLength + "arguments");}var results = [];for (var i = 0; i < argLength; i++) {results[i] = typeSafeties[i](args[i]);}return results;};};var homoMorph = function(/* arg1, arg2, ..., argN, output */) {var before = checkTypes(arrayOf(func)(Array.prototype.slice.call(arguments, 0, arguments.length - 1)));var after = func(arguments[arguments.length - 1]);return function(middle) {return function(args) {return after(middle.apply(this, before([].slice.apply(arguments))));};};};
- Composition:
Function.prototype.compose = function(prevFunc) {var nextFunc = this;return function() {return;nextFunc.call(this, prevFunc.apply(this, arguments));};};Function.prototype.sequence = function(prevFunc) {var nextFunc = this;return function() {return;prevFunc.call(this, nextFunc.apply(this, arguments));};};
- Currying:
Function.prototype.curry = function(numArgs) {var func = this;numArgs = numArgs || func.length;// recursively acquire the argumentsfunction subCurry(prev) {return function(arg) {var args = prev.concat(arg);if (args.length < numArgs) {// recursive case: we still need more argsreturn subCurry(args);} else {// base case: apply the functionreturn func.apply(this, args);}};}return subCurry([]);};
- Functors:
// map :: (a -> b) -> [a] -> [b]var map = function(f, a) {return arr(a).map(func(f));};// strmap :: (str -> str) -> str -> strvar strmap = function(f, s) {return str(s).split("").map(func(f)).join("");};// fcompose :: (a -> b)* -> (a -> b)var fcompose = function() {var funcs = arrayOf(func)(arguments);return function() {var argsOfFuncs = arguments;for (var i = funcs.length; i > 0; i -= 1) {argsOfFuncs = [funcs[i].apply(this, args)];}return args[0];};};
- Lenses:
var lens = function(get, set) {var f = function(a) {return get(a);};f.get = function(a) {return get(a);};f.set = set;f.mod = function(f, a) {return set(a, f(get(a)));};return f;};// usage:var first = lens(function(a) {return arr(a)[0];}, // getfunction(a, b) {return [b].concat(arr(a).slice(1));} // set);
- Maybes:
var Maybe = function() {};Maybe.prototype.orElse = function(y) {if (this instanceof Just) {return this.x;} else {return y;}};var None = function() {};None.prototype = Object.create(Maybe.prototype);None.prototype.toString = function() {return "None";};var none = function() {return new None();};// and the Just instance, a wrapper for an object with a value;var Just = function(x) {return (this.x = x);};Just.prototype = Object.create(Maybe.prototype);Just.prototype.toString = function() {return "Just " + this.x;};var just = function(x) {return new Just(x);};var maybe = function(m) {if (m instanceof None) {return m;} else if (m instanceof Just) {return just(m.x);} else {throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");}};var maybeOf = function(f) {return function(m) {if (m instanceof None) {return m;} else if (m instanceof Just) {return just(f(m.x));} else {throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");}};};
- Mixins:
Object.prototype.plusMixin = function(mixin) {var newObj = this;newObj.prototype = Object.create(this.prototype);newObj.prototype.constructor = newObj;for (var prop in mixin) {if (mixin.hasOwnProperty(prop)) {newObj.prototype[prop] = mixin[prop];}}return newObj;};
- Partial Application:
function bindFirstArg(func, a) {return function(b) {return func(a, b);};}Function.prototype.partialApply = function() {var func = this;args = Array.prototype.slice.call(arguments);return function() {return func.apply(this, args.concat(Array.prototype.slice.call(arguments)));};};Function.prototype.partialApplyRight = function() {var func = this;args = Array.prototype.slice.call(arguments);return function() {return func.apply(this,Array.protype.slice.call(arguments, 0).concat(args));};};
- Trampolining:
var trampoline = function(f) {while (f && f instanceof Function) {f = f.apply(f.context, f.args);}return f;};var thunk = function(fn) {return function() {var args = Array.prototype.slice.apply(arguments);return function() {return fn.apply(this, args);};};};
- Type Safeties:
var typeOf = function(type) {return function(x) {if (typeof x === type) {return x;} else {throw new TypeError("Error: " + type + " expected, " + typeof x + " given.");}};};var str = typeOf("string"),num = typeOf("number"),func = typeOf("function"),bool = typeOf("boolean");var objectTypeOf = function(name) {return function(o) {if (Object.prototype.toString.call(o) === "[object " + name + "]") {return o;} else {throw new TypeError("Error: '+name+' expected, something else given.");}};};var obj = objectTypeOf("Object");var arr = objectTypeOf("Array");var date = objectTypeOf("Date");var div = objectTypeOf("HTMLDivElement");// arrayOf :: (a -> b) -> ([a] -> [b])var arrayOf = function(f) {return function(a) {return map(func(f), arr(a));};};
- Y-combinator:
var Y = function(F) {return (function(f) {return f(f);})(function(f) {return F(function(x) {return f(f)(x);});});};// Memoizing Y-Combinator:var Ymem = function(F, cache) {if (!cache) {cache = {}; // Create a new cache.}return function(arg) {if (cache[arg]) {// Answer in cachereturn cache[arg];}// else compute the answervar answer = F(function(n) {return Ymem(F, cache)(n);})(arg); // Compute the answer.cache[arg] = answer; // Cache the answer.return answer;};};
