-
-
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'))) |
Lol, I was just looking at that one.
On Thu, Jul 12, 2012 at 7:33 PM, Kyle Putnam < ***@***.*** > wrote:
Look out, there's a nasty bug in curry. I've fixed it in a later revision,
but see if you can find it :-)
---
Reply to this email directly or view it on GitHub:
https://gist.github.com/3101939
##
_Chad Elliott_
*816-645-1414
*@THEtheChad http://twitter.com/THEtheChad
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); }
var xyz = curry(function(x,y,z) { return x + y + z; });
xyz(1)(2)(3) //=> 6
xyz(1)(2)(0) //=> 6 ... whoops!
These functions are blowing my mind =P
On Thu, Jul 12, 2012 at 7:39 PM, Kyle Putnam < ***@***.*** > wrote:
``` javascript
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); }
var xyz = curry(function(x,y,z) { return x + y + z; });
xyz(1)(2)(3) //=> 6
xyz(1)(2)(0) //=> 6 ... whoops!
```
---
Reply to this email directly or view it on GitHub:
https://gist.github.com/3101939
##
_Chad Elliott_
*816-645-1414
*@THEtheChad http://twitter.com/THEtheChad
var curry = function(f,n) {
if (n === 0) return f;
var args = []
, last = function(x) { args.push(x); var c = _.clone(args); args.length = 0; return f.apply(null, c); }
, fold = function(g) { return function(x) { args.push(x); return g; }};
return _.reduce(_.range(n-1), fold, last); }
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
Look out, there's a nasty bug in curry. I've fixed it in a later revision, but see if you can find it :-)