简介

附录介绍了使用JavaScript进行函数式编程的常用功能:

  • Array Functions:
  1. var flatten = function(arrays) {
  2. return arrays.reduce(function(p, n) {
  3. return p.concat(n);
  4. });
  5. };
  6. var invert = function(arr) {
  7. return arr.map(function(x, i, a) {
  8. return a[a.length - (i + 1)];
  9. });
  10. };
  • Binding Functions:
  1. var bind = Function.prototype.call.bind(Function.prototype.bind);
  2. var call = bind(Function.prototype.call, Function.prototype.call);
  3. var apply = bind(Function.prototype.call, Function.prototype.apply);
  • Category Theory:
  1. var checkTypes = function(typeSafeties) {
  2. arrayOf(func)(arr(typeSafeties));
  3. var argLength = typeSafeties.length;
  4. return function(args) {
  5. arr(args);
  6. if (args.length != argLength) {
  7. throw new TypeError("Expected " + argLength + "arguments");
  8. }
  9. var results = [];
  10. for (var i = 0; i < argLength; i++) {
  11. results[i] = typeSafeties[i](args[i]);
  12. }
  13. return results;
  14. };
  15. };
  16. var homoMorph = function(/* arg1, arg2, ..., argN, output */) {
  17. var before = checkTypes(
  18. arrayOf(func)(
  19. Array.prototype.slice.call(arguments, 0, arguments.length - 1)
  20. )
  21. );
  22. var after = func(arguments[arguments.length - 1]);
  23. return function(middle) {
  24. return function(args) {
  25. return after(middle.apply(this, before([].slice.apply(arguments))));
  26. };
  27. };
  28. };
  • Composition:
  1. Function.prototype.compose = function(prevFunc) {
  2. var nextFunc = this;
  3. return function() {
  4. return;
  5. nextFunc.call(this, prevFunc.apply(this, arguments));
  6. };
  7. };
  8. Function.prototype.sequence = function(prevFunc) {
  9. var nextFunc = this;
  10. return function() {
  11. return;
  12. prevFunc.call(this, nextFunc.apply(this, arguments));
  13. };
  14. };
  • Currying:
  1. Function.prototype.curry = function(numArgs) {
  2. var func = this;
  3. numArgs = numArgs || func.length;
  4. // recursively acquire the arguments
  5. function subCurry(prev) {
  6. return function(arg) {
  7. var args = prev.concat(arg);
  8. if (args.length < numArgs) {
  9. // recursive case: we still need more args
  10. return subCurry(args);
  11. } else {
  12. // base case: apply the function
  13. return func.apply(this, args);
  14. }
  15. };
  16. }
  17. return subCurry([]);
  18. };
  • Functors:
  1. // map :: (a -> b) -> [a] -> [b]
  2. var map = function(f, a) {
  3. return arr(a).map(func(f));
  4. };
  5. // strmap :: (str -> str) -> str -> str
  6. var strmap = function(f, s) {
  7. return str(s)
  8. .split("")
  9. .map(func(f))
  10. .join("");
  11. };
  12. // fcompose :: (a -> b)* -> (a -> b)
  13. var fcompose = function() {
  14. var funcs = arrayOf(func)(arguments);
  15. return function() {
  16. var argsOfFuncs = arguments;
  17. for (var i = funcs.length; i > 0; i -= 1) {
  18. argsOfFuncs = [funcs[i].apply(this, args)];
  19. }
  20. return args[0];
  21. };
  22. };
  • Lenses:
  1. var lens = function(get, set) {
  2. var f = function(a) {
  3. return get(a);
  4. };
  5. f.get = function(a) {
  6. return get(a);
  7. };
  8. f.set = set;
  9. f.mod = function(f, a) {
  10. return set(a, f(get(a)));
  11. };
  12. return f;
  13. };
  14. // usage:
  15. var first = lens(
  16. function(a) {
  17. return arr(a)[0];
  18. }, // get
  19. function(a, b) {
  20. return [b].concat(arr(a).slice(1));
  21. } // set
  22. );
  • Maybes:
  1. var Maybe = function() {};
  2. Maybe.prototype.orElse = function(y) {
  3. if (this instanceof Just) {
  4. return this.x;
  5. } else {
  6. return y;
  7. }
  8. };
  9. var None = function() {};
  10. None.prototype = Object.create(Maybe.prototype);
  11. None.prototype.toString = function() {
  12. return "None";
  13. };
  14. var none = function() {
  15. return new None();
  16. };
  17. // and the Just instance, a wrapper for an object with a value;
  18. var Just = function(x) {
  19. return (this.x = x);
  20. };
  21. Just.prototype = Object.create(Maybe.prototype);
  22. Just.prototype.toString = function() {
  23. return "Just " + this.x;
  24. };
  25. var just = function(x) {
  26. return new Just(x);
  27. };
  28. var maybe = function(m) {
  29. if (m instanceof None) {
  30. return m;
  31. } else if (m instanceof Just) {
  32. return just(m.x);
  33. } else {
  34. throw new TypeError(
  35. "Error: Just or None expected, " + m.toString() + " given."
  36. );
  37. }
  38. };
  39. var maybeOf = function(f) {
  40. return function(m) {
  41. if (m instanceof None) {
  42. return m;
  43. } else if (m instanceof Just) {
  44. return just(f(m.x));
  45. } else {
  46. throw new TypeError(
  47. "Error: Just or None expected, " + m.toString() + " given."
  48. );
  49. }
  50. };
  51. };
  • Mixins:
  1. Object.prototype.plusMixin = function(mixin) {
  2. var newObj = this;
  3. newObj.prototype = Object.create(this.prototype);
  4. newObj.prototype.constructor = newObj;
  5. for (var prop in mixin) {
  6. if (mixin.hasOwnProperty(prop)) {
  7. newObj.prototype[prop] = mixin[prop];
  8. }
  9. }
  10. return newObj;
  11. };
  • Partial Application:
  1. function bindFirstArg(func, a) {
  2. return function(b) {
  3. return func(a, b);
  4. };
  5. }
  6. Function.prototype.partialApply = function() {
  7. var func = this;
  8. args = Array.prototype.slice.call(arguments);
  9. return function() {
  10. return func.apply(this, args.concat(Array.prototype.slice.call(arguments)));
  11. };
  12. };
  13. Function.prototype.partialApplyRight = function() {
  14. var func = this;
  15. args = Array.prototype.slice.call(arguments);
  16. return function() {
  17. return func.apply(
  18. this,
  19. Array.protype.slice.call(arguments, 0).concat(args)
  20. );
  21. };
  22. };
  • Trampolining:
  1. var trampoline = function(f) {
  2. while (f && f instanceof Function) {
  3. f = f.apply(f.context, f.args);
  4. }
  5. return f;
  6. };
  7. var thunk = function(fn) {
  8. return function() {
  9. var args = Array.prototype.slice.apply(arguments);
  10. return function() {
  11. return fn.apply(this, args);
  12. };
  13. };
  14. };
  • Type Safeties:
  1. var typeOf = function(type) {
  2. return function(x) {
  3. if (typeof x === type) {
  4. return x;
  5. } else {
  6. throw new TypeError(
  7. "Error: " + type + " expected, " + typeof x + " given."
  8. );
  9. }
  10. };
  11. };
  12. var str = typeOf("string"),
  13. num = typeOf("number"),
  14. func = typeOf("function"),
  15. bool = typeOf("boolean");
  16. var objectTypeOf = function(name) {
  17. return function(o) {
  18. if (Object.prototype.toString.call(o) === "[object " + name + "]") {
  19. return o;
  20. } else {
  21. throw new TypeError("Error: '+name+' expected, something else given.");
  22. }
  23. };
  24. };
  25. var obj = objectTypeOf("Object");
  26. var arr = objectTypeOf("Array");
  27. var date = objectTypeOf("Date");
  28. var div = objectTypeOf("HTMLDivElement");
  29. // arrayOf :: (a -> b) -> ([a] -> [b])
  30. var arrayOf = function(f) {
  31. return function(a) {
  32. return map(func(f), arr(a));
  33. };
  34. };
  • Y-combinator:
  1. var Y = function(F) {
  2. return (function(f) {
  3. return f(f);
  4. })(function(f) {
  5. return F(function(x) {
  6. return f(f)(x);
  7. });
  8. });
  9. };
  10. // Memoizing Y-Combinator:
  11. var Ymem = function(F, cache) {
  12. if (!cache) {
  13. cache = {}; // Create a new cache.
  14. }
  15. return function(arg) {
  16. if (cache[arg]) {
  17. // Answer in cache
  18. return cache[arg];
  19. }
  20. // else compute the answer
  21. var answer = F(function(n) {
  22. return Ymem(F, cache)(n);
  23. })(arg); // Compute the answer.
  24. cache[arg] = answer; // Cache the answer.
  25. return answer;
  26. };
  27. };