-
-
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'))) |
Author
THEtheChad
commented
Jul 13, 2012
Rewrote the function to clone the arguments list and reset the size to 0. You get better performance by altering the length instead of creating a new object because the browser doesn't have to perform garbage collection on the old one.
Yep, that seems to fix the bug.
var curried = function(f, n, args) {
return (args.length >= n)
? f.apply(null, args)
: function() { return curried(f, n, args.concat(slice(arguments))); }; };
var slice = function(args, n) { return Array.prototype.slice.call(args, n || 0); }
var curry = function(f, n) { return (n <= 0) ? f : curried(f, n, []); }
Though this alternate definition has a nice property: no burdensome syntax penalty when you don't need partial application.
var xyz = curry(function(x,y,z) { return x + y + z; }, 3);
xyz(1)(2)(3) == xyz(1,2,3);
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