-
-
Save THEtheChad/3101939 to your computer and use it in GitHub Desktop.
Functional combinators in JavaScript
This file contains 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 u = (function() { | |
var id = function(x) { return x; } | |
var constant = function(x) { return function() { return x; }} | |
var ap = function(f,g) { return function(x) { return f(x)(g(x)); }} | |
var slice = function(args, n) { return Array.prototype.slice.call(args, n || 0); } | |
var attr = function(name) { return function(object) { return object[name]; }} | |
var bind = function(ctx) { return function(method) { return function() { return method.apply(ctx, slice(arguments)); }}} | |
var call = function(args) { return function(func) { return func.apply(null, slice(args)); }} | |
var flip = function(func) { return function(a) { return function(b) { return func(b)(a); }}} | |
var apply = function() { return call(slice(arguments)); } | |
var compose = function(f,g) { return function(x) { return f(g(x)); }} | |
var method = function(name) { return ap(bind, attr(name)); } | |
var invoke = function(name) { return compose(call(slice(arguments, 1)), method(name)); } | |
var curry = function(f,n) { | |
if (n === 0) return f; | |
var args = [] | |
, last = function(x) { args.push(x); return f.apply(null, args); } | |
, fold = function(g) { return function(x) { args.push(x); return g; }}; | |
return _.reduce(_.range(n-1), fold, last); } | |
return ({id: id, constant: constant, ap: ap, slice: slice, attr: attr, bind: bind, call: call, | |
flip: flip, apply: apply, compose: compose, method: method, curry: curry, invoke: invoke}); | |
})(); | |
var op = ({ | |
'add': u.curry(function(x,y) { return x + y; }, 2) | |
, 'sub': u.curry(function(x,y) { return x - y; }, 2) | |
, 'mul': u.curry(function(x,y) { return x * y; }, 2) | |
, 'div': u.curry(function(x,y) { return x / y; }, 2) | |
, 'mod': u.curry(function(x,y) { return x % y; }, 2) | |
, 'eq': u.curry(function(x,y) { return x === y; }, 2) | |
, 'ne': u.curry(function(x,y) { return x !== y; }, 2) | |
, 'gt': u.curry(function(x,y) { return x > y; }, 2) | |
, 'lt': u.curry(function(x,y) { return x < y; }, 2) | |
, 'lte': u.curry(function(x,y) { return x <= y; }, 2) | |
, 'gte': u.curry(function(x,y) { return x >= y; }, 2) | |
, 'or': u.curry(function(x,y) { return x || y; }, 2) | |
, 'and': u.curry(function(x,y) { return x && y; }, 2) | |
, 'xor': u.curry(function(x,y) { return x !== y; }, 2) | |
, 'not': function(x) { return !x; } | |
, 'typeof': function(x) { return typeof x; } | |
, 'instanceof': function(x) { return function(y) { return x instanceof y; }} | |
}); | |
// Where does 'x' occur in each element? | |
_.map(['xbc', 'dxf', 'ghx'], u.invoke('indexOf', 'x')) | |
// What's the remainder of each element divided by 3? | |
_.map([12345, 23456, 56789], u.flip(op.mod)(3)) | |
// What's the length of each element? | |
_.map(['abc', 'def', 'ghi'], u.attr('length')) | |
// Does anyone have an 'o'? | |
_.any(['xbc', 'dxf', 'ghx'], u.compose(op.ne(-1), u.invoke('indexOf', 'o'))) |
Oh, I noticed your fix is still incomplete. This is a classic case where mutable state can really be a pain in the ass.
var xyz = curry(function(x,y,z) { return x + y + z; }, 3);
var z = xyz(1)(2);
z(3) //=> 3
z(3) //=> NaN
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yep, that seems to fix the bug.
Though this alternate definition has a nice property: no burdensome syntax penalty when you don't need partial application.