javascript combinators, the “six” edition
TRANSCRIPT
-
2016 Reginald Braithwaite. Some rights reserved. 1
http://creativecommons.org/licenses/by-sa/4.0/
-
JavaScript Combinatorsthe "six" edi*on
2016 Reginald Braithwaite. Some rights reserved. 2
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 3
http://creativecommons.org/licenses/by-sa/4.0/
-
we'll talk about
Using combinators for decomposi2on and composi2on
2016 Reginald Braithwaite. Some rights reserved. 4
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 5
http://creativecommons.org/licenses/by-sa/4.0/
-
and we'll think about
Making responsibili/es and rela/onships explicit
2016 Reginald Braithwaite. Some rights reserved. 6
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 7
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 8
http://creativecommons.org/licenses/by-sa/4.0/
-
Decomposi)on
2016 Reginald Braithwaite. Some rights reserved. 9
http://creativecommons.org/licenses/by-sa/4.0/
-
We decompose en%%es to make discreet responsibili%es explicit
2016 Reginald Braithwaite. Some rights reserved. 10
http://creativecommons.org/licenses/by-sa/4.0/
-
a monolith
Parse.User.logIn("user", "pass", { success: function (user) { query.find({ success: function (users) { users[0].save({ key: value }, { success: function (user) { currentUser = user; } }); } }); }});
2016 Reginald Braithwaite. Some rights reserved. 11
http://creativecommons.org/licenses/by-sa/4.0/
-
decomposi)on by extrac)ng func)ons
let assignCurrentUser = (user) => { currentUser = user; };
let saveFirstUser = (users) => users[0].save({ key: value }, { success: assignCurrentUser });
let logUserIn = (user) => query.find({ success: saveFirstUser });
Parse.User.logIn("user", "pass", { success: logUserIn });
2016 Reginald Braithwaite. Some rights reserved. 12
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 13
http://creativecommons.org/licenses/by-sa/4.0/
-
Composi'on
2016 Reginald Braithwaite. Some rights reserved. 14
http://creativecommons.org/licenses/by-sa/4.0/
-
We compose en%%es to make the rela%onships between them explicit
2016 Reginald Braithwaite. Some rights reserved. 15
http://creativecommons.org/licenses/by-sa/4.0/
-
promises explicitly compose asynchronous func3ons
let findUser = (user) => query.find();
let saveFirstUser = (user) => users[0].save({ key: value });
let assignCurrentUser = (user) => { currentUser = user; };
Parse.User.logIn("user", "pass") .then(findUser) .then(saveFirstUser) .then(assignCurrentUser);
2016 Reginald Braithwaite. Some rights reserved. 16
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 17
http://creativecommons.org/licenses/by-sa/4.0/
-
Decomposi)on is about en))es
2016 Reginald Braithwaite. Some rights reserved. 18
http://creativecommons.org/licenses/by-sa/4.0/
-
Composi'on is about rela'onships
2016 Reginald Braithwaite. Some rights reserved. 19
http://creativecommons.org/licenses/by-sa/4.0/
-
Back to decomposi.on
2016 Reginald Braithwaite. Some rights reserved. 20
http://creativecommons.org/licenses/by-sa/4.0/
-
extrac'ng named func'ons
The most obvious form of decomposi2on
2016 Reginald Braithwaite. Some rights reserved. 21
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 22
http://creativecommons.org/licenses/by-sa/4.0/
-
Extrac'ng func'ons works from the inside-out
2016 Reginald Braithwaite. Some rights reserved. 23
http://creativecommons.org/licenses/by-sa/4.0/
-
extrac'ng named func'ons
Decomposi)on of Implementa)on
2016 Reginald Braithwaite. Some rights reserved. 24
http://creativecommons.org/licenses/by-sa/4.0/
-
Let's look at something else
2016 Reginald Braithwaite. Some rights reserved. 25
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 26
http://creativecommons.org/licenses/by-sa/4.0/
-
pluck: "A convenient version of what is perhaps the most common use-case for map,
extrac8ng a list of property values."
2016 Reginald Braithwaite. Some rights reserved. 27
http://creativecommons.org/licenses/by-sa/4.0/
-
let pluck = (collection, property) => collection.map( (obj) => obj[property] );
var deStijl = [ {name: 'Theo van Doesburg', occupation: 'theorist'}, {name: 'Piet Mondriaan', occupation: 'painter'}, {name: 'Gerrit Rietveld', occupation: 'architect'}];
pluck(deStijl, 'name') //=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"]
2016 Reginald Braithwaite. Some rights reserved. 28
http://creativecommons.org/licenses/by-sa/4.0/
-
func%ons have interfaces
pluck's interface has two parts: The collec0on, and the property
2016 Reginald Braithwaite. Some rights reserved. 29
http://creativecommons.org/licenses/by-sa/4.0/
-
manually decomposing pluck's interface
var deStijl = [ {name: 'Theo van Doesburg', occupation: 'theorist'}, {name: 'Piet Mondriaan', occupation: 'painter'}, {name: 'Gerrit Rietveld', occupation: 'architect'}];
let pluckFrom = (collection) => (property) => pluck(collection, property);
pluckFrom(deStijl)('name') //=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"]
2016 Reginald Braithwaite. Some rights reserved. 30
http://creativecommons.org/licenses/by-sa/4.0/
-
manually decomposing pluck's interface
let pluckWith = (property) => (collection) => pluck(collection, property);
pluckWith('name')(deStijl) //=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"]
2016 Reginald Braithwaite. Some rights reserved. 31
http://creativecommons.org/licenses/by-sa/4.0/
-
pluckFrom and pluckWith par'ally apply pluck
2016 Reginald Braithwaite. Some rights reserved. 32
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 33
http://creativecommons.org/licenses/by-sa/4.0/
-
Par$al applica$on decomposes func$ons from the outside-in
2016 Reginald Braithwaite. Some rights reserved. 34
http://creativecommons.org/licenses/by-sa/4.0/
-
par$al applica$on
Decomposi)on of Interface
2016 Reginald Braithwaite. Some rights reserved. 35
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 36
http://creativecommons.org/licenses/by-sa/4.0/
-
Decorators
2016 Reginald Braithwaite. Some rights reserved. 37
http://creativecommons.org/licenses/by-sa/4.0/
-
A decorator is a higher-order func1on that takes a func1on, and returns another func1on that adds
to or modifies its argument's behaviour.
2016 Reginald Braithwaite. Some rights reserved. 38
http://creativecommons.org/licenses/by-sa/4.0/
-
par$al applica$on decomposes a func$on from the outside-in
let pluck = (collection, property) => collection.map( (obj) => obj[property] );
// decomposes into:
let pluckFrom = (collection) => (property) => pluck(collection, property);
2016 Reginald Braithwaite. Some rights reserved. 39
http://creativecommons.org/licenses/by-sa/4.0/
-
extract closed-over binding
let pluckFrom = (collection) => (property) => pluck(collection, property);
// extract `pluck`:
let ____ = (pluck, collection) => (property) => pluck(collection, property);
// rename:
let leftApply = (fn, a) => (b) => fn(a, b);
2016 Reginald Braithwaite. Some rights reserved. 40
http://creativecommons.org/licenses/by-sa/4.0/
-
using leftApply
let pluckFrom = (collection) => leftApply(pluck, collection);
// again:
let pluckFrom = leftApply(leftApply, pluck);
// again
let pluckFrom = leftApply(leftApply, leftApply)(pluck);
2016 Reginald Braithwaite. Some rights reserved. 41
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 42
http://creativecommons.org/licenses/by-sa/4.0/
-
hmmmmWhat is leftApply(leftApply,
leftApply)?
2016 Reginald Braithwaite. Some rights reserved. 43
http://creativecommons.org/licenses/by-sa/4.0/
-
back to par*al applica*on
let rightApply = (fn, b) => (a) => fn(a, b);
let pluckWith = (property) => (pluck, property);
// again:
let pluckWith = leftApply(rightApply, pluck);
// again:
let pluckWith = leftApply(leftApply, rightApply)(pluck);
2016 Reginald Braithwaite. Some rights reserved. 44
http://creativecommons.org/licenses/by-sa/4.0/
-
combinators that decompose func2ons
// leftApply(leftApply, leftApply)
let Istarstar = (a) => (b) => (c) => a(b, c);
let pluckFrom = Istarstar(pluck);
// leftApply(leftApply, rightApply)
let C = (a) => (b) => (c) => a(c, b)
let pluckWith = C(pluck);
2016 Reginald Braithwaite. Some rights reserved. 45
http://creativecommons.org/licenses/by-sa/4.0/
-
more decomposi+on with combinators
let get = (object, property) => object[property];
get({name: 'Gerrit Rietveld'}, 'name') //=> Gerrit Rietveld
let getWith = C(get);
let nameOf = getWith('name');
nameOf({name: 'Gerrit Rietveld'}) //=> Gerrit Rietveld
2016 Reginald Braithwaite. Some rights reserved. 46
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 47
http://creativecommons.org/licenses/by-sa/4.0/
-
get is a func)on taking two arguments
getWith is a decomposi+on of get that names one part
nameOf is a decomposi+on of get that names and specifies one part
2016 Reginald Braithwaite. Some rights reserved. 48
http://creativecommons.org/licenses/by-sa/4.0/
-
last one
let map = (collection, fn) => collection.map(fn);
let mapWith = C(map);
let namesOf = mapWith(nameOf);
2016 Reginald Braithwaite. Some rights reserved. 49
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 50
http://creativecommons.org/licenses/by-sa/4.0/
-
map is a higher-order func0on
mapWith is a decomposi+on of map that names one part
namesOf is a decomposi+on of map that names and specifies one part
2016 Reginald Braithwaite. Some rights reserved. 51
http://creativecommons.org/licenses/by-sa/4.0/
-
Back to composi,on
2016 Reginald Braithwaite. Some rights reserved. 52
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 53
http://creativecommons.org/licenses/by-sa/4.0/
-
Simple Composi+on
2016 Reginald Braithwaite. Some rights reserved. 54
http://creativecommons.org/licenses/by-sa/4.0/
-
let compose = (a, b) =>(c) => a(b(c));
2016 Reginald Braithwaite. Some rights reserved. 55
http://creativecommons.org/licenses/by-sa/4.0/
-
compose in ac&on
var deStijl = [ {name: 'Theo van Doesburg', occupation: 'theorist'}, {name: 'Piet Mondriaan', occupation: 'painter'}, {name: 'Gerrit Rietveld', occupation: 'architect'}];
let pluckWith = compose(mapWith, getWith);
let namesOf = pluckWith('name');
namesOf(deStijl) //=> ["Theo van Doesburg","Piet Mondriaan","Gerrit Rietveld"]
2016 Reginald Braithwaite. Some rights reserved. 56
http://creativecommons.org/licenses/by-sa/4.0/
-
pluckWith = compose(mapWith, getWith);
2016 Reginald Braithwaite. Some rights reserved. 57
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 58
http://creativecommons.org/licenses/by-sa/4.0/
-
reminder:
We compose en**es to make the rela*onships between them explicit
2016 Reginald Braithwaite. Some rights reserved. 59
http://creativecommons.org/licenses/by-sa/4.0/
-
More Composi+on
2016 Reginald Braithwaite. Some rights reserved. 60
http://creativecommons.org/licenses/by-sa/4.0/
-
let mix = (...ingredients) => console.log('mixing', ...ingredients);
let bake = () => console.log('baking');
let cool = () => console.log('cooling');
let makeBread = (...ingredients) => { mix(...ingredients); bake(); cool();}
2016 Reginald Braithwaite. Some rights reserved. 61
http://creativecommons.org/licenses/by-sa/4.0/
-
composi'on with before
let before = (fn, decoration) => (...args) => { decoration(...args); return fn(...args); };
let bakeBread = before(bake, mix);
let makeBread = (...ingredients) => { bakeBread(); cool();}
2016 Reginald Braithwaite. Some rights reserved. 62
http://creativecommons.org/licenses/by-sa/4.0/
-
before makes the )me rela)onship between two func)ons explicit
2016 Reginald Braithwaite. Some rights reserved. 63
http://creativecommons.org/licenses/by-sa/4.0/
-
composi'on with after
let after = (fn, decoration) => (...args) => { let returnValue = fn(...args); decoration(...args); return returnValue; };
let bakeBread = before(bake, mix);
let makeBread = after(bakeBread, cool);
2016 Reginald Braithwaite. Some rights reserved. 64
http://creativecommons.org/licenses/by-sa/4.0/
-
after also makes the +me rela+onship between two func+ons
explicit
2016 Reginald Braithwaite. Some rights reserved. 65
http://creativecommons.org/licenses/by-sa/4.0/
-
decomposing before
let beforeWith = (decoration) => rightApply(before, decoration);
let mixBefore = beforeWith(mix);
let bakeBread = mixBefore(bake);
2016 Reginald Braithwaite. Some rights reserved. 66
http://creativecommons.org/licenses/by-sa/4.0/
-
decomposing after
let afterWith = (decoration) => rightApply(after, decoration);
let coolAfter = afterWith(after);
let makeBread = coolAfter(bakeBread);
2016 Reginald Braithwaite. Some rights reserved. 67
http://creativecommons.org/licenses/by-sa/4.0/
-
beforeWith and afterWith are combinators that turn func1ons into decorators that compose behaviour
2016 Reginald Braithwaite. Some rights reserved. 68
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 69
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 70
http://creativecommons.org/licenses/by-sa/4.0/
-
JavaScript invoca-ons are coloured
2016 Reginald Braithwaite. Some rights reserved. 71
http://creativecommons.org/licenses/by-sa/4.0/
-
coloured decorators
let before = (fn, decoration) => function (...args) { decoration.apply(this, args); return fn.apply(this, args); };
let after = (fn, decoration) => function (...args) { let returnValue = fn.apply(this, args); decoration.apply(this, args); return returnValue; };
2016 Reginald Braithwaite. Some rights reserved. 72
http://creativecommons.org/licenses/by-sa/4.0/
-
Why coloured decorators ma0er
2016 Reginald Braithwaite. Some rights reserved. 73
http://creativecommons.org/licenses/by-sa/4.0/
-
bread, revisited
class Bread { constructor (...ingredients) { this.ingredients = ingredients; }
mix () { console.log('mixing', ...this.ingredients); };
bake () { console.log('baking'); }
cool () { console.log('cooling'); }}
2016 Reginald Braithwaite. Some rights reserved. 74
http://creativecommons.org/licenses/by-sa/4.0/
-
bread, revisited
class Bread {
// ...
make () { this.mix(); this.bake(); this.cool(); }}
2016 Reginald Braithwaite. Some rights reserved. 75
http://creativecommons.org/licenses/by-sa/4.0/
-
Classes can be decorated too
2016 Reginald Braithwaite. Some rights reserved. 76
http://creativecommons.org/licenses/by-sa/4.0/
-
decorateMethodWith
const decorateMethodWith = (decorator, ...methodNames) => (clazz) => { for (let methodName of methodNames) { const method = clazz.prototype[methodName];
Object.defineProperty(clazz.prototype, methodName, { value: decorator(method), writable: true }); } return clazz; };
2016 Reginald Braithwaite. Some rights reserved. 77
http://creativecommons.org/licenses/by-sa/4.0/
-
beforeAll and afterAll
const beforeAll = (decorator, ...methodNames) => decorateMethodWith((method) => before(method, decorator), ...methodNames),
afterAll = (decorator, ...methodNames) => decorateMethodWith((method) => after(method, decorator), ...methodNames);
2016 Reginald Braithwaite. Some rights reserved. 78
http://creativecommons.org/licenses/by-sa/4.0/
-
be#er bread
let invoke = (methodName) => function (...args) { return this[methodName](...args);}
let BetterBread = beforeAll(invoke('mix'), 'make')( afterAll(invoke('cool'), 'make')( class { // ...
make () { this.bake(); } } ) );
2016 Reginald Braithwaite. Some rights reserved. 79
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 80
http://creativecommons.org/licenses/by-sa/4.0/
-
Looking Forward
2016 Reginald Braithwaite. Some rights reserved. 81
http://creativecommons.org/licenses/by-sa/4.0/
-
ES.who-knows-when
2016 Reginald Braithwaite. Some rights reserved. 82
http://creativecommons.org/licenses/by-sa/4.0/
-
be#er bread with class decorator sugar
let invoke = (methodName) => function (...args) { return this[methodName](...args);}
@beforeAll(invoke('mix'), 'make')@afterAll(invoke('cool'), 'make')class AwesomeBread {
// ...
make () { this.bake(); }}
2016 Reginald Braithwaite. Some rights reserved. 83
http://creativecommons.org/licenses/by-sa/4.0/
-
method decorators
let methodDecorator = (decorator) => function (target, name, descriptor) { descriptor.value = decorator(descriptor.value); }
let invokeBefore = (methodName) => methodDecorator( (methodBody) => before(methodBody, invoke(methodName)) );
let invokeAfter = (methodName) => methodDecorator( (methodBody) => after(methodBody, invoke(methodName)) );
2016 Reginald Braithwaite. Some rights reserved. 84
http://creativecommons.org/licenses/by-sa/4.0/
-
be#er make
class Bread {
// ...
@invokeBefore('mix') @invokeAfter('cool') make () { this.bake(); }}
2016 Reginald Braithwaite. Some rights reserved. 85
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 86
http://creativecommons.org/licenses/by-sa/4.0/
-
What have we seen so far?
2016 Reginald Braithwaite. Some rights reserved. 87
http://creativecommons.org/licenses/by-sa/4.0/
-
What have we seen so far? Extract func,on
Promise interface
Par,al applica,on
Extract closed-over binding
(more!)
2016 Reginald Braithwaite. Some rights reserved. 88
http://creativecommons.org/licenses/by-sa/4.0/
-
What have we seen so far? Simple composi,on
Composi,on decorators
Class decorators
Method decorators
2016 Reginald Braithwaite. Some rights reserved. 89
http://creativecommons.org/licenses/by-sa/4.0/
-
Don't worry about the details!
2016 Reginald Braithwaite. Some rights reserved. 90
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 91
http://creativecommons.org/licenses/by-sa/4.0/
-
it's all the same idea
Decomposi)on makes responsibili)es explicit
2016 Reginald Braithwaite. Some rights reserved. 92
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 93
http://creativecommons.org/licenses/by-sa/4.0/
-
and it's all the same idea
Composi'on makes rela'onships explicit
2016 Reginald Braithwaite. Some rights reserved. 94
http://creativecommons.org/licenses/by-sa/4.0/
-
These ideas ma*er
2016 Reginald Braithwaite. Some rights reserved. 95
http://creativecommons.org/licenses/by-sa/4.0/
-
There are only two hard problems in Computer Science: Cache
invalida9on, and naming things.
2016 Reginald Braithwaite. Some rights reserved. 96
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 97
http://creativecommons.org/licenses/by-sa/4.0/
-
Naming en))es is hard because you have to figure out which en))es
need to be named
2016 Reginald Braithwaite. Some rights reserved. 98
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 99
http://creativecommons.org/licenses/by-sa/4.0/
-
Naming rela+onships is hard because you have to figure out which rela+onships need to be
named
2016 Reginald Braithwaite. Some rights reserved. 100
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 101
http://creativecommons.org/licenses/by-sa/4.0/
-
Combinators do not make naming easy
2016 Reginald Braithwaite. Some rights reserved. 102
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 103
http://creativecommons.org/licenses/by-sa/4.0/
-
Combinators give us a language for naming things in code
2016 Reginald Braithwaite. Some rights reserved. 104
http://creativecommons.org/licenses/by-sa/4.0/
-
2016 Reginald Braithwaite. Some rights reserved. 105
http://creativecommons.org/licenses/by-sa/4.0/
-
Do not followin the footsteps of the sages.
2016 Reginald Braithwaite. Some rights reserved. 106
http://creativecommons.org/licenses/by-sa/4.0/
-
Seek what they sought.
2016 Reginald Braithwaite. Some rights reserved. 107
http://creativecommons.org/licenses/by-sa/4.0/
-
Reg BraithwaitePagerDuty, Inc.
raganwald.com@raganwald
2016 Reginald Braithwaite. Some rights reserved. 108
http://raganwald.comhttps://twitter.com/raganwaldhttp://creativecommons.org/licenses/by-sa/4.0/