Last active
June 16, 2016 08:48
-
-
Save CrossEye/5179280 to your computer and use it in GitHub Desktop.
An alternate form of functional composition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------------------------------------ | |
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"); | |
//------------------------------------------------------------------------------ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Depends for now on
Array.protoype.forEach
, although that could easily change. Other than that, should be usable in any environment.