Skip to content

Instantly share code, notes, and snippets.

@jfarmer
Last active January 4, 2017 08:43
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jfarmer/543cafa2ec76fc6f648a to your computer and use it in GitHub Desktop.
Save jfarmer/543cafa2ec76fc6f648a to your computer and use it in GitHub Desktop.
It's like lisp, in JavaScript!
var isEven = function(n) { return n % 2 === 0; },
isOdd = not(isEven);
var add = curry(function(a,b) { return a + b; }),
inc = add(1),
incAll = map(inc);
var times = curry(function(a,b) { return a * b; }),
double = times(2),
doubleAll = map(double);
console.log("" + cons(cons(1,2), cons(3,4)), "((1 2) (3 4))");
console.log("" + incAll(list(1,2,3)), "(2 (3 (4 null)))");
var evens = list(0,2,4,6,8),
odds = list(1,3,5,7,9);
var areAnyEven = any(isEven),
areAnyOdd = any(isOdd),
areAllOdd = all(isOdd);
console.log("" + areAnyOdd(odds), "true");
console.log("" + areAnyOdd(evens), "false");
console.log("" + areAllOdd(odds), "true");
console.log("" + areAllOdd(evens), "false");
console.log("" + areAnyOdd(doubleAll(odds)), "false");
console.log("" + areAnyEven(incAll(evens)), "false");
console.log("" + areAllOdd(incAll(evens)), "true");
var onlyEvens = filter(isEven),
numbers = list(1,2,3,4);
console.log("" + onlyEvens(numbers), "(2 (4 null))");
var argsToArray = Function.prototype.call.bind(Array.prototype.slice);
var curry = function(fn, filled) {
var filled = filled || [];
var arity = fn.length;
return function() {
var args = argsToArray(arguments);
var newFilled = filled.concat(args);
if (newFilled.length >= arity) {
return fn.apply(this, newFilled);
} else {
return curry(fn, newFilled);
}
}
};
// A helper method to call
var toString = function(o) {
if (o !== null && typeof(o) !== 'object' && typeof(o) !== 'function') {
return o.toString();
} else {
return "" + o;
}
};
var pairify = function(fn) {
fn.pair = true;
fn.toString = function() {
if (isEmpty(this)) { return "()"; }
var head = first(this),
tail = rest(this);
// FIXME: This displays cons(1, cons(2, 3)) as (1 (2 3)) vs. (1 2 3)
return "(" + toString(head) + " " + toString(tail) + ")";
};
return fn;
};
var cons = curry(function(x,y) {
return pairify(function(fn) { return fn(x, y); });
});
var first = function(fn) {
return fn(function(x, y) { return x });
};
var rest = function(fn) {
return fn(function(x, y) { return y });
};
// e.g., list(1,2,3,4) == cons(1, cons(2, cons(3, cons(4, null))))
var list = function() {
var args = argsToArray(arguments);
return (args.length === 0) ? null : cons(args.shift(), list.apply(null, args));
};
var isAtom = function(x) {
return (x !== null) && (x !== undefined) && !x.pair;
};
var isPair = function(x) {
return x && x.pair;
};
var isNull = function(x) {
return x === null;
}
var isZero = function(x) {
return x === 0;
}
var isEmpty = function(lst) {
return isNull(lst) || isNull(first(lst));
};
var foldl = curry(function(fn, acc, lst) {
return isEmpty(lst) ? acc : foldl(fn, fn(acc, first(lst)), rest(lst));
});
var foldr = curry(function(fn, acc, lst) {
return isEmpty(lst) ? acc : fn(first(lst), foldr(fn, acc, rest(lst)));
});
var map = curry(function(fn, lst) {
return foldr(function(acc, lst) { return cons(fn(acc), lst) }, null, lst);
});
var filter = curry(function(fn, lst) {
return foldr(function(acc, lst) { return fn(acc) ? cons(acc, lst) : lst }, null, lst);
});
var append = curry(function(to, lst) {
return isEmpty(to) ? lst : cons(first(to), append(rest(to), lst));
});
var reverse = function(lst) {
return isEmpty(lst) ? lst : append(reverse(rest(lst)), list(first(lst)));
};
var not = curry(function(pred, arg) {
return !pred(arg)
});
var any = curry(function(pred, lst) {
return isEmpty(lst) ? false : (pred(first(lst)) || any(pred, rest(lst)));
});
var all = curry(function(pred, lst) {
return !any(not(pred), lst);
});
var flip = function(fn) {
return curry(function(a,b) { return fn(b,a) });
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment