Instantly share code, notes, and snippets.

@CrossEye /first.js
Last active Jun 16, 2016

Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner

CrossEye commented Mar 18, 2013

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