# 简介

附录介绍了使用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 arguments
  function subCurry(prev) {
    return function(arg) {
      var args = prev.concat(arg);
      if (args.length < numArgs) {
        // recursive case: we still need more args
        return subCurry(args);
      } else {
        // base case: apply the function
        return 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 -> str
var 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];
  }, // get
  function(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 cache
      return cache[arg];
    }
    // else compute the answer
    var answer = F(function(n) {
      return Ymem(F, cache)(n);
    })(arg); // Compute the answer.
    cache[arg] = answer; // Cache the answer.
    return answer;
  };
};