^ https://www.flickr.com/photos/andy_li/4378437185
^ "A Unified Theory of JavaScript Style, Part I"
^ https://www.flickr.com/photos/andy_li/4378437185
^ The "backbone" of this talk is to explain the combinators make software that is easier to factor and refactor by creating an "aglgebra" where functions form a semigroup, rather than using code, which is more flexibible but creates a sparse group
^ https://www.flickr.com/photos/popculturegeek/5991539415
^ "talk about Combinators and decorators"
^ https://www.flickr.com/photos/popculturegeek/5991539415
^ https://www.flickr.com/photos/creative_stock/3521504068
^ "think about flexibility and decluttering"
^ https://www.flickr.com/photos/creative_stock/3521504068
^ https://www.flickr.com/photos/scelera/3036797409
^ "We compose entities to create new entities"
^ https://www.flickr.com/photos/scelera/3036797409
^ https://www.flickr.com/photos/justinbaeder/5317820857
^ Not all entities "fit together"
^ https://www.flickr.com/photos/justinbaeder/5317820857
^ worse, some are time-dependent
^ https://www.flickr.com/photos/michale/2744016741
^ "Homogeneous interfaces create dense spaces"
^ https://www.flickr.com/photos/michale/2744016741
^ Example: Integers and addition, multiplication: very dense
^ https://www.flickr.com/photos/stretta/5572576057
^ "Heterogeneous interfaces create sparse spaces"
^ https://www.flickr.com/photos/stretta/5572576057
^ counter-example: imagine an operation on integers wher emost operations were invalid. you'd have to engage in a complex search to find the path from any one integer to another.
^ https://www.flickr.com/photos/michale/2744016741
^ https://www.flickr.com/photos/stretta/5572576057
^ "Dense is more flexible than sparse"
^ https://www.flickr.com/photos/michale/2744016741
^ https://www.flickr.com/photos/stretta/5572576057
^ https://www.flickr.com/photos/dipfan/110857520
^ https://www.flickr.com/photos/nathanoliverphotography/9604185186
^ "Sparse can be quicker to grasp"
^ https://www.flickr.com/photos/dipfan/110857520
^ https://www.flickr.com/photos/nathanoliverphotography/9604185186
^ we'll give an example, showing how we have to build up.
^ https://www.flickr.com/photos/joao_trindade/4362414729
^ "enough with the math!"
^ https://www.flickr.com/photos/joao_trindade/4362414729
^ https://www.flickr.com/photos/mediterraneaaan/12756787045
^ "pluck"
pluck: "A convenient version of what is perhaps the most common use-case for map: extracting a list of property values."
^ https://www.flickr.com/photos/mediterraneaaan/12756787045
^ underscorejs.org
function pluck (mappable, key) {
return mappable.map(function (obj) {
return obj[key];
});
};
function pluckWith (key, mappable) {
return pluck(mappable, key);
};
var stooges = [
{name: 'moe', age: 40},
{name: 'larry', age: 50},
{name: 'curly', age: 60}];
pluckWith('name', stooges);
//=> ["moe", "larry", "curly"]
function flip (fn) {
return function flipped (a, b) {
return fn.call(this, b, a);
}
}
function arrow (a, b) {
return "" + a + " -> " + b;
}
flip(arrow)("x", "y")
//=> 'y -> x'
^ http://jsfiddle.net/raganwald/wFRP8/
^ https://www.flickr.com/photos/saramarlowe/8170948596
^ Another unary combinator
^ https://www.flickr.com/photos/saramarlowe/8170948596
function curry (fn) {
return function curried (a, optionalB) {
if (arguments.length > 1) {
return fn.call(this, a, optionalB);
}
else return function partiallyApplied (b) {
return fn.call(this, a, b);
}
}
}
^ binary only
var curriedArrow = curry(arrow);
//=> [function]
curriedArrow('finger')('moon')
//=> 'finger -> moon'
^ http://raganwald.com/2013/03/07/currying-and-partial-application.html
var taoism = curry(arrow)('finger');
//=> [function]
taoism('moon')
//=> 'finger -> moon'
^ http://raganwald.com/2013/03/07/currying-and-partial-application.html
^ https://www.flickr.com/photos/genista/4449316
^ "Partial application transforms binary operations into unary operations"
^ https://www.flickr.com/photos/genista/4449316
^ makes the interface more homogeneous
function get (object, property) {
return object[property];
}
get({foo: 1}, 'foo')
//=> 1
var getWith = curry(flip(get));
getWith('foo')({foo: 1})
//=> 1
function map (mappable, fn) {
return mappable.map(fn, this);
}
function double (n) {
return n * 2;
}
map([1, 2, 3], double)
//=> [2, 4, 6]
var mapWith = curry(flip(map));
mapWith(double, [1, 2, 3]);
//=> [2, 4, 6]
var doubleAll = mapWith(double);
doubleAll([1, 2, 3])
//=> [2, 4, 6]
almost there...
function pluckWith (attr) {
return mapWith(getWith(attr));
}
^ https://www.flickr.com/photos/ctaweb/8487304182
^ "Compose"
^ https://www.flickr.com/photos/ctaweb/8487304182
function compose (a, b) {
return function composed (c) {
return a(b(c));
}
}
var pluckWith = compose(mapWith, getWith);
var pluckWith = compose(mapWith, getWith);
//// versus ////
function pluck (mappable, key) {
return mappable.map(function (obj) {
return obj[key];
});
};
function pluckWith (key, mappable) {
return pluck(mappable, key);
};
^ teases apart heterogeneous factoring
^ asterix: with care, they can be easier to read.
function Cake () {}
extend(Cake.prototype, {
mix: function () {
// mix ingredients together
return this;
},
rise: function (duration) {
// let the ingredients rise
return this;
},
bake: function () {
// do some baking
return this;
}
});
function fluent (methodBody) {
return function fluentized () {
methodBody.apply(this, arguments);
return this;
}
}
function Cake () {}
extend(Cake.prototype, {
mix: fluent( function () {
// mix ingredients together
}),
rise: fluent( function (duration) {
// let the ingredients rise
}),
bake: fluent(function () {
// do some baking
})
});
extend(Cake.prototype, {
mix: fluent( function () {
// mix ingredients together
}),
rise: fluent( function (duration) {
this.mix();
// let the ingredients rise
}),
bake: fluent(function () {
this.mix();
// do some baking
})
});
var before = curry(
function decorate (decoration, method) {
return function decoratedWithBefore () {
decoration.apply(this, arguments);
return method.apply(this, arguments);
};
}
);
var mixFirst = before(function () {
this.mix()
});
extend(Cake.prototype, {
// Other methods...
mix: fluent( function () {
// mix ingredients together
}),
rise: fluent( mixFirst( function (duration) {
// let the ingredients rise
})),
bake: fluent( mixFirst( function () {
// do some baking
}))
});
var after = curry(
function decorate (decoration, method) {
return function decoratedWithAfter () {
var returnValue = method.apply(this, arguments);
decoration.apply(this, arguments);
return returnValue;
};
}
);
var around = curry(
function decorate (decoration, method) {
return function decoratedWithAround () {
var methodPrepended = [method].concat(
[].slice.call(arguments, 0)
);
return decoration.apply(this, methodPrepended);
};
}
);
var maybe = around(function (fn, value) {
if (value != null) {
return fn.call(this, value);;
}
});
maybe(double)(2)
//=> 4
maybe(double)(null)
//=> undefined
function provided (guard) {
return around(function () {
var fn = arguments[0],
values = [].slice.call(arguments, 1);
if (guard.apply(this, values)) {
return fn.apply(this, values);
}
});
}
var maybe = provided( function (value) {
return value != null;
});
function not (fn) {
return function notted () {
return !fn.apply(this, arguments)
}
}
var except = compose(provided, not);
var maybe = except( function (value) {
return value == null;
});
^ https://www.flickr.com/photos/68112440@N07/6210847796
^ "lessons"
^ https://www.flickr.com/photos/68112440@N07/6210847796
^ https://www.flickr.com/photos/paulmccoubrie/6792412657
^ "Combinators increase code flexibility and require increased mental flexibility"
^ https://www.flickr.com/photos/paulmccoubrie/6792412657
^ https://www.flickr.com/photos/terry_wha/107810852
^ "Decorators declutter secondary concerns"
^ https://www.flickr.com/photos/terry_wha/107810852
^ https://www.flickr.com/photos/suburbanbloke/723665503
^ "Do not follow in the footsteps of the sages. Seek what they sought"
^ https://www.flickr.com/photos/suburbanbloke/723665503
NDC Conference, Oslo, Norway, June 5, 2014