Skip to content

Instantly share code, notes, and snippets.

@luislee818
Created January 4, 2016 09:32
Show Gist options
  • Save luislee818/9d259f50eda997bb0344 to your computer and use it in GitHub Desktop.
Save luislee818/9d259f50eda997bb0344 to your computer and use it in GitHub Desktop.
Sources of the Functional JavaScript book, combined
// Chap 1
function splat(fun) {
return function(array) {
return fun.apply(null, array);
};
}
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements([1, 2]);
//=> 3
function unsplat(fun) {
return function() {
return fun.call(null, _.toArray(arguments));
};
}
var joinElements = unsplat(function(array) { return array.join(' ') });
joinElements(1, 2);
//=> "1 2"
joinElements('-', '$', '/', '!', ':');
//=> "- $ / ! :"
function parseAge(age) {
if (!_.isString(age)) throw new Error("Expecting a string");
var a;
console.log("Attempting to parse an age");
a = parseInt(age, 10);
if (_.isNaN(a)) {
console.log(["Could not parse age:", age].join(' '));
a = 0;
}
return a;
}
function fail(thing) {
throw new Error(thing);
}
function warn(thing) {
console.log(["WARNING:", thing].join(' '));
}
function note(thing) {
console.log(["NOTE:", thing].join(' '));
}
function parseAge(age) {
if (!_.isString(age)) fail("Expecting a string");
var a;
note("Attempting to parse an age");
a = parseInt(age, 10);
if (_.isNaN(a)) {
warn(["Could not parse age:", age].join(' '));
a = 0;
}
return a;
}
var letters = ['a', 'b', 'c'];
letters[1];
//=> 'b'
function naiveNth(a, index) {
return a[index];
}
function isIndexed(data) {
return _.isArray(data) || _.isString(data);
}
function nth(a, index) {
if (!_.isNumber(index)) fail("Expected a number as the index");
if (!isIndexed(a)) fail("Not supported on non-indexed type");
if ((index < 0) || (index > a.length - 1))
fail("Index value is out of bounds");
return a[index];
}
function second(a) {
return nth(a, 1);
}
function compareLessThanOrEqual(x, y) {
if (x < y) return -1;
if (y < x) return 1;
return 0;
}
[2, 3, -1, -6, 0, -108, 42, 10].sort(compareLessThanOrEqual);
//=> [-108, -6, -1, 0, 2, 3, 10, 42]
function lessOrEqual(x, y) {
return x <= y;
}
function comparator(pred) {
return function(x, y) {
if (truthy(pred(x, y)))
return -1;
else if (truthy(pred(y, x)))
return 1;
else
return 0;
};
};
function lameCSV(str) {
return _.reduce(str.split("\n"), function(table, row) {
table.push(_.map(row.split(","), function(c) { return c.trim()}));
return table;
}, []);
};
var peopleTable = lameCSV("name,age,hair\nMerble,35,red\nBob,64,blonde");
peopleTable;
//=> [["name", "age", "hair"],
// ["Merble", "35", "red"],
// ["Bob", "64", "blonde"]]
function selectNames(table) {
return _.rest(_.map(table, _.first));
}
function selectAges(table) {
return _.rest(_.map(table, second));
}
function selectHairColor(table) {
return _.rest(_.map(table, function(row) {
return nth(row, 2);
}));
}
var mergeResults = _.zip;
function existy(x) { return x != null };
function truthy(x) { return (x !== false) && existy(x) };
function doWhen(cond, action) {
if(truthy(cond))
return action();
else
return undefined;
}
function executeIfHasField(target, name) {
return doWhen(existy(target[name]), function() {
var result = _.result(target, name);
console.log(['The result is', result].join(' '));
return result;
});
}
// Chap 2
var lyrics = [];
for (var bottles = 99; bottles > 0; bottles--) {
lyrics.push(bottles + " bottles of beer on the wall");
lyrics.push(bottles + " bottles of beer");
lyrics.push("Take one down, pass it around");
if (bottles > 1) {
lyrics.push((bottles - 1) + " bottles of beer on the wall.");
}
else {
lyrics.push("No more bottles of beer on the wall!");
}
}
function lyricSegment(n) {
return _.chain([])
.push(n + " bottles of beer on the wall")
.push(n + " bottles of beer")
.push("Take one down, pass it around")
.tap(function(lyrics) {
if (n > 1)
lyrics.push((n - 1) + " bottles of beer on the wall.");
else
lyrics.push("No more bottles of beer on the wall!");
})
.value();
}
function song(start, end, lyricGen) {
return _.reduce(_.range(start,end,-1),
function(acc,n) {
return acc.concat(lyricGen(n));
}, []);
}
var nums = [1,2,3,4,5];
function doubleAll(array) {
return _.map(array, function(n) { return n*2 });
}
doubleAll(nums);
//=> [2, 4, 6, 8, 10]
function average(array) {
var sum = _.reduce(array, function(a, b) { return a+b });
return sum / _.size(array);
}
average(nums);
//=> 3
/* grab only even numbers in nums */
function onlyEven(array) {
return _.filter(array, function(n) {
return (n%2) === 0;
});
}
onlyEven(nums);
//=> [2, 4]
function allOf(/* funs */) {
return _.reduceRight(arguments, function(truth, f) {
return truth && f();
}, true);
}
function anyOf(/* funs */) {
return _.reduceRight(arguments, function(truth, f) {
return truth || f();
}, false);
}
function complement(pred) {
return function() {
return !pred.apply(null, _.toArray(arguments));
};
}
function cat() {
var head = _.first(arguments);
if (existy(head))
return head.concat.apply(head, _.rest(arguments));
else
return [];
}
cat([1,2,3], [4,5], [6,7,8]);
//=> [1, 2, 3, 4, 5, 6, 7, 8]
function construct(head, tail) {
return cat([head], _.toArray(tail));
}
construct(42, [1,2,3]);
//=> [42, 1, 2, 3]
function mapcat(fun, coll) {
return cat.apply(null, _.map(coll, fun));
}
function butLast(coll) {
return _.toArray(coll).slice(0, -1);
}
function interpose (inter, coll) {
return butLast(mapcat(function(e) {
return construct(e, [inter]);
},
coll));
}
var zombie = {name: "Bub", film: "Day of the Dead"};
_.keys(zombie);
//=> ["name", "film"]
_.values(zombie);
//=> ["Bub", "Day of the Dead"]
var library = [{title: "SICP", isbn: "0262010771", ed: 1},
{title: "SICP", isbn: "0262510871", ed: 2},
{title: "Joy of Clojure", isbn: "1935182641", ed: 1}];
_.findWhere(library, {title: "SICP", ed: 2});
//=> {title: "SICP", isbn: "0262510871", ed: 2}
function project(table, keys) {
return _.map(table, function(obj) {
return _.pick.apply(null, construct(obj, keys));
});
};
function rename(obj, newNames) {
return _.reduce(newNames, function(o, nu, old) {
if (_.has(obj, old)) {
o[nu] = obj[old];
return o;
}
else
return o;
},
_.omit.apply(null, construct(obj, _.keys(newNames))));
};
function as(table, newNames) {
return _.map(table, function(obj) {
return rename(obj, newNames);
});
};
function restrict(table, pred) {
return _.reduce(table, function(newTable, obj) {
if (truthy(pred(obj)))
return newTable;
else
return _.without(newTable, obj);
}, table);
};
// Chap 3
function makeEmptyObject() {
return new Object();
}
var globals = {};
function makeBindFun(resolver) {
return function(k, v) {
var stack = globals[k] || [];
globals[k] = resolver(stack, v);
return globals;
};
}
var stackBinder = makeBindFun(function(stack, v) {
stack.push(v);
return stack;
});
var stackUnbinder = makeBindFun(function(stack) {
stack.pop();
return stack;
});
var dynamicLookup = function(k) {
var slot = globals[k] || [];
return _.last(slot);
};
function f() { return dynamicLookup('a'); };
function g() { stackBinder('a', 'g'); return f(); };
f();
//=> 1
g();
//=> 'g'
globals;
// {a: [1, "g"], b: [100]}
function strangeIdentity(n) {
// intentionally strange
for(var i=0; i<n; i++);
return i;
}
strangeIdentity(138);
//=> 138
function strangeIdentity(n) {
var i;
for(i=0; i<n; i++);
return i;
}
function strangerIdentity(n) {
// intentionally stranger still
for(this['i'] = 0; this['i']<n; this['i']++);
return this['i'];
}
strangerIdentity(108);
//=> 108
function createScaleFunction(FACTOR) {
return function(v) {
return _.map(v, function(n) {
return (n * FACTOR);
});
};
}
var scale10 = createScaleFunction(10);
scale10([1,2,3]);
//=> [10, 20, 30]
function createWeirdScaleFunction(FACTOR) {
return function(v) {
this['FACTOR'] = FACTOR;
var captures = this;
return _.map(v, _.bind(function(n) {
return (n * this['FACTOR']);
}, captures));
};
}
var scale10 = createWeirdScaleFunction(10);
scale10.call({}, [5,6,7]);
//=> [50, 60, 70];
function makeAdder(CAPTURED) {
return function(free) {
return free + CAPTURED;
};
}
var add10 = makeAdder(10);
add10(32);
//=> 42
function averageDamp(FUN) {
return function(n) {
return average([n, FUN(n)]);
}
}
var averageSq = averageDamp(function(n) { return n * n });
averageSq(10);
//=> 55
function complement(PRED) {
return function() {
return !PRED.apply(null, _.toArray(arguments));
};
}
function isEven(n) { return (n%2) === 0 }
var isOdd = complement(isEven);
isOdd(2);
//=> false
isOdd(413);
//=> true
function plucker(FIELD) {
return function(obj) {
return (obj && obj[FIELD]);
};
}
var best = {title: "Infinite Jest", author: "DFW"};
var getTitle = plucker('title');
getTitle(best);
//=> "Infinite Jest"
var books = [{title: "Chthon"}, {stars: 5}, {title: "Botchan"}];
var third = plucker(2);
third(books);
//=> {title: "Botchan"}
// Chap 4
var people = [{name: "Fred", age: 65}, {name: "Lucy", age: 36}];
_.max(people, function(p) { return p.age });
//=> {name: "Fred", age: 65}
function finder(valueFun, bestFun, coll) {
return _.reduce(coll, function(best, current) {
var bestValue = valueFun(best);
var currentValue = valueFun(current);
return (bestValue === bestFun(bestValue, currentValue)) ? best : current;
});
}
function best(fun, coll) {
return _.reduce(coll, function(x, y) {
return fun(x, y) ? x : y;
});
}
best(function(x,y) { return x > y }, [1,2,3,4,5]);
//=> 5
function repeat(times, VALUE) {
return _.map(_.range(times), function() { return VALUE; });
}
repeat(4, "Major");
//=> ["Major", "Major", "Major", "Major"]
function repeatedly(times, fun) {
return _.map(_.range(times), fun);
}
repeatedly(3, function() {
return Math.floor((Math.random()*10)+1);
});
//=> [1, 3, 8]
function iterateUntil(fun, check, init) {
var ret = [];
var result = fun(init);
while (check(result)) {
ret.push(result);
result = fun(result);
}
return ret;
};
function always(VALUE) {
return function() {
return VALUE;
};
};
function invoker (NAME, METHOD) {
return function(target /* args ... */) {
if (!existy(target)) fail("Must provide a target");
var targetMethod = target[NAME];
var args = _.rest(arguments);
return doWhen((existy(targetMethod) && METHOD === targetMethod), function() {
return targetMethod.apply(target, args);
});
};
};
var rev = invoker('reverse', Array.prototype.reverse);
_.map([[1,2,3]], rev);
//=> [[3,2,1]]
function uniqueString(len) {
return Math.random().toString(36).substr(2, len);
};
uniqueString(10);
//=> "3rm6ww5w0x"
function uniqueString(prefix) {
return [prefix, new Date().getTime()].join('');
};
uniqueString("argento");
//=> "argento1356107740868"
function makeUniqueStringFunction(start) {
var COUNTER = start;
return function(prefix) {
return [prefix, COUNTER++].join('');
}
};
var uniqueString = makeUniqueStringFunction(0);
uniqueString("dari");
//=> "dari0"
uniqueString("dari");
//=> "dari1"
var generator = {
count: 0,
uniqueString: function(prefix) {
return [prefix, this.count++].join('');
}
};
generator.uniqueString("bohr");
//=> bohr0
generator.uniqueString("bohr");
//=> bohr1
var omgenerator = (function(init) {
var COUNTER = init;
return {
uniqueString: function(prefix) {
return [prefix, COUNTER++].join('');
}
};
})(0);
omgenerator.uniqueString("lichking-");
//=> "lichking-0"
var nums = [1,2,3,null,5];
_.reduce(nums, function(total, n) { return total * n });
//=> 0
function fnull(fun /*, defaults */) {
var defaults = _.rest(arguments);
return function(/* args */) {
var args = _.map(arguments, function(e, i) {
return existy(e) ? e : defaults[i];
});
return fun.apply(null, args);
};
};
var safeMult = fnull(function(total, n) { return total * n }, 1, 1);
_.reduce(nums, safeMult);
//=> 30
function defaults(d) {
return function(o, k) {
var val = fnull(_.identity, d[k]);
return o && val(o[k]);
};
}
function doSomething(config) {
var lookup = defaults({critical: 108});
return lookup(config, 'critical');
}
doSomething({critical: 9});
//=> 9
doSomething({});
//=> 108
function checker(/* validators */) {
var validators = _.toArray(arguments);
return function(obj) {
return _.reduce(validators, function(errs, check) {
if (check(obj))
return errs;
else
return _.chain(errs).push(check.message).value();
}, []);
};
}
function validator(message, fun) {
var f = function(/* args */) {
return fun.apply(fun, arguments);
};
f['message'] = message;
return f;
}
function aMap(obj) {
return _.isObject(obj);
}
var checkCommand = checker(validator("must be a map", aMap));
function hasKeys() {
var KEYS = _.toArray(arguments);
var fun = function(obj) {
return _.every(KEYS, function(k) {
return _.has(obj, k);
});
};
fun.message = cat(["Must have values for keys:"], KEYS).join(" ");
return fun;
}
var checkCommand = checker(validator("must be a map", aMap), hasKeys('msg', 'type'));
// Chap 5
function dispatch(/* funs */) {
var funs = _.toArray(arguments);
var size = funs.length;
return function(target /*, args */) {
var ret = undefined;
var args = _.rest(arguments);
for (var funIndex = 0; funIndex < size; funIndex++) {
var fun = funs[funIndex];
ret = fun.apply(fun, construct(target, args));
if (existy(ret)) return ret;
}
return ret;
};
}
var str = dispatch(invoker('toString', Array.prototype.toString),
invoker('toString', String.prototype.toString));
str("a");
//=> "a"
str(_.range(10));
//=> "0,1,2,3,4,5,6,7,8,9"
function stringReverse(s) {
if (!_.isString(s)) return undefined;
return s.split('').reverse().join("");
}
stringReverse("abc");
//=> "cba"
stringReverse(1);
//=> undefined
var rev = dispatch(invoker('reverse', Array.prototype.reverse),
stringReverse);
rev([1,2,3]);
//=> [3, 2, 1]
rev("abc");
//=> "cba"
function performCommandHardcoded(command) {
var result;
switch (command.type)
{
case 'notify':
result = notify(command.message);
break;
case 'join':
result = changeView(command.target);
break;
default:
alert(command.type);
}
return result;
}
function isa(type, action) {
return function(obj) {
if (type === obj.type)
return action(obj);
}
}
var performCommand = dispatch(
isa('notify', function(obj) { return notify(obj.message) }),
isa('join', function(obj) { return changeView(obj.target) }),
function(obj) { alert(obj.type) });
var performAdminCommand = dispatch(
isa('kill', function(obj) { return shutdown(obj.hostname) }),
performCommand);
var performTrialUserCommand = dispatch(
isa('join', function(obj) { alert("Cannot join until approved") }),
performCommand);
function rightAwayInvoker() {
var args = _.toArray(arguments);
var method = args.shift();
var target = args.shift();
return method.apply(target, args);
}
rightAwayInvoker(Array.prototype.reverse, [1,2,3])
//=> [3, 2, 1]
function leftCurryDiv(n) {
return function(d) {
return n/d;
};
}
function rightCurryDiv(d) {
return function(n) {
return n/d;
};
}
function curry(fun) {
return function(arg) {
return fun(arg);
};
}
function curry2(fun) {
return function(secondArg) {
return function(firstArg) {
return fun(firstArg, secondArg);
};
};
}
function div(n, d) { return n / d }
var div10 = curry2(div)(10);
div10(50);
//=> 5
var parseBinaryString = curry2(parseInt)(2);
parseBinaryString("111");
//=> 7
parseBinaryString("10");
//=> 2
var plays = [{artist: "Burial", track: "Archangel"},
{artist: "Ben Frost", track: "Stomp"},
{artist: "Ben Frost", track: "Stomp"},
{artist: "Burial", track: "Archangel"},
{artist: "Emeralds", track: "Snores"},
{artist: "Burial", track: "Archangel"}];
_.countBy(plays, function(song) {
return [song.artist, song.track].join(" - ");
});
//=> {"Ben Frost - Stomp": 2,
// "Burial - Archangel": 3,
// "Emeralds - Snores": 1}
function songToString(song) {
return [song.artist, song.track].join(" - ");
}
var songCount = curry2(_.countBy)(songToString);
songCount(plays);
//=> {"Ben Frost - Stomp": 2,
// "Burial - Archangel": 3,
// "Emeralds - Snores": 1}
function curry3(fun) {
return function(last) {
return function(middle) {
return function(first) {
return fun(first, middle, last);
};
};
};
};
var songsPlayed = curry3(_.uniq)(false)(songToString);
songsPlayed(plays);
//=> [{artist: "Burial", track: "Archangel"},
// {artist: "Ben Frost", track: "Stomp"},
// {artist: "Emeralds", track: "Snores"}]
function toHex(n) {
var hex = n.toString(16);
return (hex.length < 2) ? [0, hex].join(''): hex;
}
function rgbToHexString(r, g, b) {
return ["#", toHex(r), toHex(g), toHex(b)].join('');
}
rgbToHexString(255, 255, 255);
//=> "#ffffff"
var blueGreenish = curry3(rgbToHexString)(255)(200);
blueGreenish(0);
//=> "#00c8ff"
var greaterThan = curry2(function (lhs, rhs) { return lhs > rhs });
var lessThan = curry2(function (lhs, rhs) { return lhs < rhs });
var withinRange = checker(
validator("arg must be greater than 10", greaterThan(10)),
validator("arg must be less than 20", lessThan(20)));
function divPart(n) {
return function(d) {
return n / d;
};
}
var over10Part = divPart(10);
over10Part(2);
//=> 5
function partial1(fun, arg1) {
return function(/* args */) {
var args = construct(arg1, arguments);
return fun.apply(fun, args);
};
}
function partial2(fun, arg1, arg2) {
return function(/* args */) {
var args = cat([arg1, arg2], arguments);
return fun.apply(fun, args);
};
}
var div10By2 = partial2(div, 10, 2)
div10By2()
//=> 5
function partial(fun /*, pargs */) {
var pargs = _.rest(arguments);
return function(/* arguments */) {
var args = cat(pargs, _.toArray(arguments));
return fun.apply(fun, args);
};
}
var zero = validator("cannot be zero", function(n) { return 0 === n
});
var number = validator("arg must be a number", _.isNumber);
function sqr(n) {
if (!number(n)) throw new Error(number.message);
if (zero(n)) throw new Error(zero.message);
return n * n;
}
function condition1(/* validators */) {
var validators = _.toArray(arguments);
return function(fun, arg) {
var errors = mapcat(function(isValid) {
return isValid(arg) ? [] : [isValid.message];
}, validators);
if (!_.isEmpty(errors))
throw new Error(errors.join(", "));
return fun(arg);
};
}
var sqrPre = condition1(
validator("arg must not be zero", complement(zero)),
validator("arg must be a number", _.isNumber));
function uncheckedSqr(n) { return n * n };
uncheckedSqr('');
//=> 0
var checkedSqr = partial1(sqrPre, uncheckedSqr);
var sillySquare = partial1(
condition1(validator("should be even", isEven)),
checkedSqr);
var validateCommand = condition1(
validator("arg must be a map", _.isObject),
validator("arg must have the correct keys", hasKeys('msg',
'type')));
var createCommand = partial(validateCommand, _.identity);
var createLaunchCommand = partial1(
condition1(
validator("arg must have the count down", hasKeys('countDown'))),
createCommand);
var isntString = _.compose(function(x) { return !x }, _.isString);
isntString([]);
//=> true
function not(x) { return !x }
var composedMapcat = _.compose(splat(cat), _.map);
composedMapcat([[1,2],[3,4],[5]], _.identity);
//=> [1, 2, 3, 4, 5]
var sqrPost = condition1(
validator("result should be a number", _.isNumber),
validator("result should not be zero", complement(zero)),
validator("result should be positive", greaterThan(0)));
var megaCheckedSqr = _.compose(partial(sqrPost, _.identity),
checkedSqr);
// Chap 6
function myLength(ary) {
if (_.isEmpty(ary))
return 0;
else
return 1 + myLength(_.rest(ary));
}
function cycle(times, ary) {
if (times <= 0)
return [];
else
return cat(ary, cycle(times - 1, ary));
}
var zipped1 = [['a', 1]];
function constructPair(pair, rests) {
return [construct(_.first(pair), _.first(rests)),
construct(second(pair), second(rests))];
}
constructPair(['a', 1],
constructPair(['b', 2],
constructPair(['c', 3], [[],[]])));
//=> [['a','b','c'],[1,2,3]]
function unzip(pairs) {
if (_.isEmpty(pairs)) return [[],[]];
return constructPair(_.first(pairs), unzip(_.rest(pairs)));
}
var influences = [
['Lisp', 'Smalltalk'],
['Lisp', 'Scheme'],
['Smalltalk', 'Self'],
['Scheme', 'JavaScript'],
['Scheme', 'Lua'],
['Self', 'Lua'],
['Self', 'JavaScript']];
function nexts(graph, node) {
if (_.isEmpty(graph)) return [];
var pair = _.first(graph);
var from = _.first(pair);
var to = second(pair);
var more = _.rest(graph);
if (_.isEqual(node, from))
return construct(to, nexts(more, node));
else
return nexts(more, node);
}
function depthSearch(graph, nodes, seen) {
if (_.isEmpty(nodes)) return rev(seen);
var node = _.first(nodes);
var more = _.rest(nodes);
if (_.contains(seen, node))
return depthSearch(graph, more, seen);
else
return depthSearch(graph,
cat(nexts(graph, node), more),
construct(node, seen));
}
function tcLength(ary, n) {
var l = n ? n : 0;
if (_.isEmpty(ary))
return l;
else
return tcLength(_.rest(ary), l + 1);
}
tcLength(_.range(10));
//=> 10
function andify(/* preds */) {
var preds = _.toArray(arguments);
return function(/* args */) {
var args = _.toArray(arguments);
var everything = function(ps, truth) {
if (_.isEmpty(ps))
return truth;
else
return _.every(args, _.first(ps))
&& everything(_.rest(ps), truth);
};
return everything(preds, true);
};
}
function orify(/* preds */) {
var preds = _.toArray(arguments);
return function(/* args */) {
var args = _.toArray(arguments);
var something = function(ps, truth) {
if (_.isEmpty(ps))
return truth;
else
return _.some(args, _.first(ps))
|| something(_.rest(ps), truth);
};
return something(preds, false);
};
}
function evenSteven(n) {
if (n === 0)
return true;
else
return oddJohn(Math.abs(n) - 1);
}
function oddJohn(n) {
if (n === 0)
return false;
else
return evenSteven(Math.abs(n) - 1);
}
function flat(ary) {
if (_.isArray(ary))
return cat.apply(cat, _.map(ary, flat));
else
return [ary];
}
function deepClone(obj) {
if (!existy(obj) || !_.isObject(obj))
return obj;
var temp = new obj.constructor();
for (var key in obj)
if (obj.hasOwnProperty(key))
temp[key] = deepClone(obj[key]);
return temp;
}
function visit(mapFun, resultFun, ary) {
if (_.isArray(ary))
return resultFun(_.map(ary, mapFun));
else
return resultFun(ary);
}
function postDepth(fun, ary) {
return visit(partial1(postDepth, fun), fun, ary);
}
function preDepth(fun, ary) {
return visit(partial1(preDepth, fun), fun, fun(ary));
}
function influencedWithStrategy(strategy, lang, graph) {
var results = [];
strategy(function(x) {
if (_.isArray(x) && _.first(x) === lang)
results.push(second(x));
return x;
}, graph);
return results;
}
function evenOline(n) {
if (n === 0)
return true;
else
return partial1(oddOline, Math.abs(n) - 1);
}
function oddOline(n) {
if (n === 0)
return false;
else
return partial1(evenOline, Math.abs(n) - 1);
}
function trampoline(fun /*, args */) {
var result = fun.apply(fun, _.rest(arguments));
while (_.isFunction(result)) {
result = result();
}
return result;
}
function isEvenSafe(n) {
if (n === 0)
return true;
else
return trampoline(partial1(oddOline, Math.abs(n) - 1));
}
function isOddSafe(n) {
if (n === 0)
return false;
else
return trampoline(partial1(evenOline, Math.abs(n) - 1));
}
function generator(seed, current, step) {
return {
head: current(seed),
tail: function() {
console.log("forced");
return generator(step(seed), current, step);
}
};
}
function genHead(gen) { return gen.head }
function genTail(gen) { return gen.tail() }
var ints = generator(0, _.identity, function(n) { return n+1 });
function genTake(n, gen) {
var doTake = function(x, g, ret) {
if (x === 0)
return ret;
else
return partial(doTake, x-1, genTail(g), cat(ret, genHead(g)));
};
return trampoline(doTake, n, gen, []);
}
function asyncGetAny(interval, urls, onsuccess, onfailure) {
var n = urls.length;
var looper = function(i) {
setTimeout(function() {
if (i >= n) {
onfailure("failed");
return;
}
$.get(urls[i], onsuccess)
.always(function() { console.log("try: " + urls[i]) })
.fail(function() {
looper(i + 1);
});
}, interval);
}
looper(0);
return "go";
}
var groupFrom = curry2(_.groupBy)(_.first);
var groupTo = curry2(_.groupBy)(second);
function influenced(graph, node) {
return _.map(groupFrom(graph)[node], second);
}
// Chap 7
var rand = partial1(_.random, 1);
function randString(len) {
var ascii = repeatedly(len, partial1(rand, 26));
return _.map(ascii, function(n) {
return n.toString(36);
}).join('');
}
PI = 3.14;
function areaOfACircle(radius) {
return PI * sqr(radius);
}
areaOfACircle(3);
//=> 28.26
function generateRandomCharacter() {
return rand(26).toString(36);
}
function generateString(charGen, len) {
return repeatedly(len, charGen).join('');
}
var composedRandomString = partial1(generateString, generateRandomCharacter);
composedRandomString(10);
//=> "j18obij1jc"
var a = [1, [10, 20, 30], 3];
var secondTwice = _.compose(second, second);
second(a) === secondTwice(a);
//=> false
function skipTake(n, coll) {
var ret = [];
var sz = _.size(coll);
for(var index = 0; index < sz; index += n) {
ret.push(coll[index]);
}
return ret;
}
function summ(ary) {
var result = 0;
var sz = ary.length;
for (var i = 0; i < sz; i++)
result += ary[i];
return result;
}
summ(_.range(1,11));
//=> 55
function summRec(ary, seed) {
if (_.isEmpty(ary))
return seed;
else
return summRec(_.rest(ary), _.first(ary) + seed);
}
summRec([], 0);
//=> 0
summRec(_.range(1,11), 0);
//=> 55
function deepFreeze(obj) {
if (!Object.isFrozen(obj))
Object.freeze(obj);
for (var key in obj) {
if (!obj.hasOwnProperty(key) || !_.isObject(obj[key]))
continue;
deepFreeze(obj[key]);
}
}
var freq = curry2(_.countBy)(_.identity);
function merge(/*args*/) {
return _.extend.apply(null, construct({}, arguments));
}
function Point(x, y) {
this._x = x;
this._y = y;
}
Point.prototype = {
withX: function(val) {
return new Point(val, this._y);
},
withY: function(val) {
return new Point(this._x, val);
}
};
function Queue(elems) {
this._q = elems;
}
Queue.prototype = {
enqueue: function(thing) {
return new Queue(cat(this._q, [thing]));
}
};
var SaferQueue = function(elems) {
this._q = _.clone(elems);
}
SaferQueue.prototype = {
enqueue: function(thing) {
return new SaferQueue(cat(this._q, [thing]));
}
};
function queue() {
return new SaferQueue(_.toArray(arguments));
}
var q = queue(1,2,3);
var enqueue = invoker('enqueue', SaferQueue.prototype.enqueue);
enqueue(q, 42);
//=> {_q: [1, 2, 3, 42]}
function Container(init) {
this._value = init;
};
Container.prototype = {
update: function(fun /*, args */) {
var args = _.rest(arguments);
var oldValue = this._value;
this._value = fun.apply(this, construct(oldValue, args));
return this._value;
}
};
var aNumber = new Container(42);
aNumber.update(function(n) { return n + 1 });
//=> 43
aNumber;
//=> {_value: 43}
// Chap 8
function createPerson() {
var firstName = "";
var lastName = "";
var age = 0;
return {
setFirstName: function(fn) {
firstName = fn;
return this;
},
setLastName: function(ln) {
lastName = ln;
return this;
},
setAge: function(a) {
age = a;
return this;
},
toString: function() {
return [firstName, lastName, age].join(' ');
}
};
}
createPerson()
.setFirstName("Mike")
.setLastName("Fogus")
.setAge(108)
.toString();
//=> "Mike Fogus 108"
var TITLE_KEY = 'titel';
// ... a whole bunch of code later
_.chain(library)
.pluck(TITLE_KEY)
.sort()
.value();
//=> [undefined, undefined, undefined]
function LazyChain(obj) {
this._calls = [];
this._target = obj;
}
LazyChain.prototype.invoke = function(methodName /*, args */) {
var args = _.rest(arguments);
this._calls.push(function(target) {
var meth = target[methodName];
return meth.apply(target, args);
});
return this;
};
LazyChain.prototype.force = function() {
return _.reduce(this._calls, function(target, thunk) {
return thunk(target);
}, this._target);
};
LazyChain.prototype.tap = function(fun) {
this._calls.push(function(target) {
fun(target);
return target;
});
return this;
}
function LazyChainChainChain(obj) {
var isLC = (obj instanceof LazyChain);
this._calls = isLC ? cat(obj._calls, []) : [];
this._target = isLC ? obj._target : obj;
}
LazyChainChainChain.prototype = LazyChain.prototype;
var longing = $.Deferred();
function go() {
var d = $.Deferred();
$.when("")
.then(function() {
setTimeout(function() {
console.log("sub-task 1");
}, 5000)
})
.then(function() {
setTimeout(function() {
console.log("sub-task 2");
}, 10000)
})
.then(function() {
setTimeout(function() {
d.resolve("done done done done");
}, 15000)
})
return d.promise();
}
function pipeline(seed /*, args */) {
return _.reduce(_.rest(arguments),
function(l,r) { return r(l); },
seed);
};
function fifth(a) {
return pipeline(a
, _.rest
, _.rest
, _.rest
, _.rest
, _.first);
}
function negativeFifth(a) {
return pipeline(a
, fifth
, function(n) { return -n });
}
negativeFifth([1,2,3,4,5,6,7,8,9]);
//=> -5
function firstEditions(table) {
return pipeline(table
, function(t) { return as(t, {ed: 'edition'}) }
, function(t) { return project(t, ['title', 'edition', 'isbn']) }
, function(t) { return restrict(t, function(book) {
return book.edition === 1;
});
});
}
var RQL = {
select: curry2(project),
as: curry2(as),
where: curry2(restrict)
};
function allFirstEditions(table) {
return pipeline(table
, RQL.as({ed: 'edition'})
, RQL.select(['title', 'edition', 'isbn'])
, RQL.where(function(book) {
return book.edition === 1;
}));
}
function actions(acts, done) {
return function (seed) {
var init = { values: [], state: seed };
var intermediate = _.reduce(acts, function (stateObj, action) {
var result = action(stateObj.state);
var values = cat(stateObj.values, [result.answer]);
return { values: values, state: result.state };
}, init);
var keep = _.filter(intermediate.values, existy);
return done(keep, intermediate.state);
};
};
function mSqr() {
return function(state) {
var ans = sqr(state);
return {answer: ans, state: ans};
}
}
var doubleSquareAction = actions(
[mSqr(),
mSqr()],
function(values) {
return values;
});
doubleSquareAction(10);
//=> [100, 10000]
function mNote() {
return function(state) {
note(state);
return {answer: undefined, state: state};
}
}
function mNeg() {
return function(state) {
return {answer: -state, state: -state};
}
}
var negativeSqrAction = actions([mSqr(), mNote(), mNeg()],
function(_, state) {
return state;
});
function lift(answerFun, stateFun) {
return function(/* args */) {
var args = _.toArray(arguments);
return function(state) {
var ans = answerFun.apply(null, construct(state, args));
var s = stateFun ? stateFun(state) : ans;
return {answer: ans, state: s};
};
};
};
var mSqr2 = lift(sqr);
var mNote2 = lift(note, _.identity);
var mNeg2 = lift(function(n) { return -n });
var negativeSqrAction2 = actions([mSqr2(), mNote2(), mNeg2()],
function(_, state) {
return state;
});
var push = lift(function(stack, e) { return construct(e, stack) });
var pop = lift(_.first, _.rest);
var stackAction = actions([
push(1),
push(2),
pop()
],
function(values, state) {
return values;
});
// Chap 9
function lazyChain(obj) {
var calls = [];
return {
invoke: function(methodName /* args */) {
var args = _.rest(arguments);
calls.push(function(target) {
var meth = target[methodName];
return meth.apply(target, args);
});
return this;
},
force: function() {
return _.reduce(calls, function(ret, thunk) {
return thunk(ret);
}, obj);
}
};
}
function deferredSort(ary) {
return lazyChain(ary).invoke('sort');
}
var deferredSorts = _.map([[2,1,3], [7,7,1], [0,9,5]], deferredSort);
//=> [<thunk>, <thunk>, <thunk>]
function force(thunk) {
return thunk.force();
}
var validateTriples = validator(
"Each array should have three elements",
function (arrays) {
return _.every(arrays, function(a) {
return a.length === 3;
});
});
var validateTripleStore = partial1(condition1(validateTriples), _.identity);
function postProcess(arrays) {
return _.map(arrays, second);
}
function processTriples(data) {
return pipeline(data
, JSON.parse
, validateTripleStore
, deferredSort
, force
, postProcess
, invoker('sort', Array.prototype.sort)
, str);
}
var reportDataPackets = _.compose(
function(s) { $('#result').text(s) },
processTriples);
function polyToString(obj) {
if (obj instanceof String)
return obj;
else if (obj instanceof Array)
return stringifyArray(obj);
return obj.toString();
}
function stringifyArray(ary) {
return ["[", _.map(ary, polyToString).join(","), "]"].join('');
}
var polyToString = dispatch(
function(s) { return _.isString(s) ? s : undefined },
function(s) { return _.isArray(s) ? stringifyArray(s) : undefined },
function(s) { return _.isObject(s) ? JSON.stringify(s) : undefined },
function(s) { return s.toString() });
Container.prototype.toString = function() {
return ["@<", polyToString(this._value), ">"].join('');
}
function ContainerClass() {}
function ObservedContainerClass() {}
function HoleClass() {}
function CASClass() {}
function TableBaseClass() {}
ObservedContainerClass.prototype = new ContainerClass();
HoleClass.prototype = new ObservedContainerClass();
CASClass.prototype = new HoleClass();
TableBaseClass.prototype = new HoleClass();
var ContainerClass = Class.extend({
init: function(val) {
this._value = val;
},
});
var c = new ContainerClass(42);
c;
//=> {_value: 42 ...}
c instanceof Class;
//=> true
var ObservedContainerClass = ContainerClass.extend({
observe: function(f) { note("set observer") },
notify: function() { note("notifying observers") }
});
var HoleClass = ObservedContainerClass.extend({
init: function(val) { this.setValue(val) },
setValue: function(val) {
this._value = val;
this.notify();
return val;
}
});
var CASClass = HoleClass.extend({
swap: function(oldVal, newVal) {
if (!_.isEqual(oldVal, this._value)) fail("No match");
return this.setValue(newVal);
}
});
function Container(val) {
this._value = val;
this.init(val);
}
Container.prototype.init = _.identity;
var HoleMixin = {
setValue: function(newValue) {
var oldVal = this._value;
this.validate(newValue);
this._value = newValue;
this.notify(oldVal, newValue);
return this._value;
}
};
var Hole = function(val) {
Container.call(this, val);
}
var ObserverMixin = (function() {
var _watchers = [];
return {
watch: function(fun) {
_watchers.push(fun);
return _.size(_watchers);
},
notify: function(oldVal, newVal) {
_.each(_watchers, function(watcher) {
watcher.call(this, oldVal, newVal);
});
return _.size(_watchers);
}
};
}());
var ValidateMixin = {
addValidator: function(fun) {
this._validator = fun;
},
init: function(val) {
this.validate(val);
},
validate: function(val) {
if (existy(this._validator) &&
!this._validator(val))
fail("Attempted to set invalid value " + polyToString(val));
}
};
_.extend(Hole.prototype
, HoleMixin
, ValidateMixin
, ObserverMixin);
var SwapMixin = {
swap: function(fun /* , args... */) {
var args = _.rest(arguments)
var newValue = fun.apply(this, construct(this._value, args));
return this.setValue(newValue);
}
};
var SnapshotMixin = {
snapshot: function() {
return deepClone(this._value);
}
};
_.extend(Hole.prototype
, HoleMixin
, ValidateMixin
, ObserverMixin
, SwapMixin
, SnapshotMixin);
var CAS = function(val) {
Hole.call(this, val);
}
var CASMixin = {
swap: function(oldVal, f) {
if (this._value === oldVal) {
this.setValue(f(this._value));
return this._value;
}
else {
return undefined;
}
}
};
_.extend(CAS.prototype
, HoleMixin
, ValidateMixin
, ObserverMixin
, SwapMixin
, CASMixin
, SnapshotMixin);
function contain(value) {
return new Container(value);
}
function hole(val /*, validator */) {
var h = new Hole();
var v = _.toArray(arguments)[1];
if (v) h.addValidator(v);
h.setValue(val);
return h;
}
var swap = invoker('swap', Hole.prototype.swap);
function cas(val /*, args */) {
var h = hole.apply(this, arguments);
var c = new CAS(val);
c._validator = h._validator;
return c;
}
var compareAndSwap = invoker('swap', CAS.prototype.swap);
function snapshot(o) { return o.snapshot() }
function addWatcher(o, fun) { o.watch(fun) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment