Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Last active June 16, 2016 08:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save CrossEye/5179280 to your computer and use it in GitHub Desktop.
Save CrossEye/5179280 to your computer and use it in GitHub Desktop.
An alternate form of functional composition.
var first = (function() {
var chainNames = ["then", "andThen", "next"];
var endChainNames = ["finally", "andFinally", "last"];
var chain = function(fn) {
var f1 = function(g) {
var func = function() {return g.call(this, fn.apply(this, arguments));};
chain(func);
return func;
};
var f2 = function(g) {
return function() {return g.call(this, fn.apply(this, arguments));};
};
chainNames.forEach(function(name) {fn[name] = f1;});
endChainNames.forEach(function(name) {fn[name] = f2;});
};
return function(f) {
var fn = function() {
return f.apply(this, arguments);
};
chain(fn);
return fn;
};
}());
// Ex: var f = first(add1).then(mult2).andThen(square).finally(negate); f(3) = -((3 + 1) * 2)^2 = -64;
// TODO?: make aliases configurable properties of `first` function?
//------------------------------------------------------------------------------
var add1 = function(x) {return x + 1;};
var mult2 = function(x) {return x * 2;};
var square = function(x) {return x * x;};
var negate = function(x) {return -x;};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
console.assert(typeof first == "function", "first should be a function");
console.assert(typeof first(add1).then == "function", "first should add `then` and related functions to a function");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
var f = first(add1);
console.assert(3 == f(2), "first should not actually change behavior of existing function");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2);
console.assert(6 == f(2), "initial composition should work appropriately");
f = first(mult2).then(add1);
console.assert(5 == f(2), "initial composition should work appropriately: order matters");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2).then(square).then(negate);
console.assert(-36 == f(2), "multiple composition should work appropriately");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2).andThen(square).next(negate);
console.assert(-36 == f(2), "composition name aliases should be available");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2).andThen(square).finally(negate);
console.assert(-36 == f(2), "composition name aliases should include finalizer");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2).andThen(square).finally(negate);
console.assert(typeof f.then == "undefined", "finalized functions should not have chainable methods");
console.assert(typeof f.finally == "undefined", "finalized functions should not have finalizer methods");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
f = first(add1).then(mult2).andThen(square).andFinally(negate);
console.assert(-36 == f(2), "finalizers should also have aliases such as `andFinally`");
console.assert(typeof f.then == "undefined", "finalized functions should not have chainable methods");
f = first(add1).then(mult2).andThen(square).last(negate);
console.assert(-36 == f(2), "finalizers should also have aliases such as `last`");
console.assert(typeof f.last == "undefined", "finalized functions should not have finalizer methods");
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
var a = function() {return this.foo;};
var b = function(x) {return x + " " + this.bar;};
var obj = {
foo: "Hello",
bar: "world",
f: first(a).then(b)
};
console.assert("Hello world" === obj.f(), "context is maintained when composed functions are used as methods");
//------------------------------------------------------------------------------
@CrossEye
Copy link
Author

Depends for now on Array.protoype.forEach, although that could easily change. Other than that, should be usable in any environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment