© 2016 Reginald Braithwaite. Some rights reserved. 1
JavaScript Combinators (2016)
© 2016 Reginald Braithwaite. Some rights reserved. 2
© 2016 Reginald Braithwaite. Some rights reserved. 3
we'll talk about
Using combinators for
decomposi2on and composi2on
© 2016 Reginald Braithwaite. Some rights reserved. 4
© 2016 Reginald Braithwaite. Some rights reserved. 5
and we'll think about
Making responsibili/es and
rela/onships explicit
© 2016 Reginald Braithwaite. Some rights reserved. 6
© 2016 Reginald Braithwaite. Some rights reserved. 7
© 2016 Reginald Braithwaite. Some rights reserved. 8
Decomposi)on
© 2016 Reginald Braithwaite. Some rights reserved. 9
We decompose en%%es to make
discreet responsibili%es explicit
© 2016 Reginald Braithwaite. Some rights reserved. 10
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
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
© 2016 Reginald Braithwaite. Some rights reserved. 13
Composi'on
© 2016 Reginald Braithwaite. Some rights reserved. 14
We compose en%%es to make the
rela%onships between them explicit
© 2016 Reginald Braithwaite. Some rights reserved. 15
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
© 2016 Reginald Braithwaite. Some rights reserved. 17
Decomposi)on is about en))es
© 2016 Reginald Braithwaite. Some rights reserved. 18
Composi'on is about rela'onships
© 2016 Reginald Braithwaite. Some rights reserved. 19
Back to decomposi.on
© 2016 Reginald Braithwaite. Some rights reserved. 20
extrac'ng named func'ons
The most obvious form of
decomposi2on
© 2016 Reginald Braithwaite. Some rights reserved. 21
© 2016 Reginald Braithwaite. Some rights reserved. 22
Extrac'ng func'ons works from the
inside-out
© 2016 Reginald Braithwaite. Some rights reserved. 23
extrac'ng named func'ons
Decomposi)on of Implementa)on
© 2016 Reginald Braithwaite. Some rights reserved. 24
Let's look at something else
© 2016 Reginald Braithwaite. Some rights reserved. 25
© 2016 Reginald Braithwaite. Some rights reserved. 26
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
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
func%ons have interfaces
pluck's interface has two parts: The
collec0on, and the property
© 2016 Reginald Braithwaite. Some rights reserved. 29
manually decomposing pluck's interface
let pluckFrom = (collection) =>
(property) => pluck(collection, property);
var deStijl = [
{name: 'Theo van Doesburg', occupation: 'theorist'},
{name: 'Piet Mondriaan', occupation: 'painter'},
{name: 'Gerrit Rietveld', occupation: 'architect'}];
pluckFrom(deStijl)('name')
//=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"]
© 2016 Reginald Braithwaite. Some rights reserved. 30
manually decomposing pluck's interface
let pluckWith = (property) =>
(collection) => pluck(collection, property);
var deStijl = [
{name: 'Theo van Doesburg', occupation: 'theorist'},
{name: 'Piet Mondriaan', occupation: 'painter'},
{name: 'Gerrit Rietveld', occupation: 'architect'}];
pluckFrom(deStijl)('name')
//=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"]
© 2016 Reginald Braithwaite. Some rights reserved. 31
pluckFrom and pluckWith
par'ally apply pluck
© 2016 Reginald Braithwaite. Some rights reserved. 32
© 2016 Reginald Braithwaite. Some rights reserved. 33
Par$al applica$on decomposes
func$ons from the outside-in
© 2016 Reginald Braithwaite. Some rights reserved. 34
par$al applica$on
Decomposi)on of Interface
© 2016 Reginald Braithwaite. Some rights reserved. 35
© 2016 Reginald Braithwaite. Some rights reserved. 36
Decorators
© 2016 Reginald Braithwaite. Some rights reserved. 37
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
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
extract closed-over binding
let pluckFrom = (collection) =>
(property) => pluck(collection, property);
// becomes:
let leftApply = (fn, a) =>
(b) => fn(a, b);
let pluckFrom = (collection) =>
leftApply(pluck, collection);
© 2016 Reginald Braithwaite. Some rights reserved. 40
extract closed-over binding
Closed over bindings can be
extracted, just like parameters
© 2016 Reginald Braithwaite. Some rights reserved. 41
extract closed-over binding
Decomposing pluckFrom into
leftApply gives us a decorator
© 2016 Reginald Braithwaite. Some rights reserved. 42
leftApply is a decorator that
decomposes a func1on's interface
© 2016 Reginald Braithwaite. Some rights reserved. 43
© 2016 Reginald Braithwaite. Some rights reserved. 44
hmmmm
What does
leftApply(leftApply,
leftApply) do?
© 2016 Reginald Braithwaite. Some rights reserved. 45
back to par*al applica*on
let rightApply = (fn, b) =>
(a) => fn(a, b);
let pluckWith = (property) =>
rightApply(pluck, property);
© 2016 Reginald Braithwaite. Some rights reserved. 46
more decomposi+on with par+al applica+on
let get = (object, property) =>
object[property];
get({name: 'Gerrit Rietveld'}, 'name')
//=> Gerrit Rietveld
let getWith = (property) => rightApply(get, property);
let nameOf = getWith('name');
nameOf({name: 'Gerrit Rietveld'})
//=> Gerrit Rietveld
© 2016 Reginald Braithwaite. Some rights reserved. 47
more decomposi+on with par+al applica+on
let map = (collection, fn) =>
collection.map(fn);
let mapWith = (fn) => rightApply(map, property);
© 2016 Reginald Braithwaite. Some rights reserved. 48
Back to composi,on
© 2016 Reginald Braithwaite. Some rights reserved. 49
© 2016 Reginald Braithwaite. Some rights reserved. 50
Simple Composi+on
© 2016 Reginald Braithwaite. Some rights reserved. 51
let compose = (a, b) =>
(c) => a(b(c));
© 2016 Reginald Braithwaite. Some rights reserved. 52
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. 53
pluckWith =
compose(mapWith, getWith);
compose wires the output of one
func/on into the input of another
© 2016 Reginald Braithwaite. Some rights reserved. 54
© 2016 Reginald Braithwaite. Some rights reserved. 55
reminder:
We compose en**es to make the
rela*onships between them explicit
© 2016 Reginald Braithwaite. Some rights reserved. 56
More Composi+on
© 2016 Reginald Braithwaite. Some rights reserved. 57
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. 58
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. 59
before makes the )me rela)onship
between two func)ons explicit
© 2016 Reginald Braithwaite. Some rights reserved. 60
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. 61
after also makes the +me
rela+onship between two func+ons
explicit
© 2016 Reginald Braithwaite. Some rights reserved. 62
decomposing before
let beforeWith = (decoration) =>
rightApply(before, decoration);
let mixBefore = beforeWith(mix);
let bakeBread = mixBefore(bake);
© 2016 Reginald Braithwaite. Some rights reserved. 63
decomposing after
let afterWith = (decoration) =>
rightApply(after, decoration);
let coolAfter = afterWith(after);
let makeBread = coolAfter(bakeBread);
© 2016 Reginald Braithwaite. Some rights reserved. 64
beforeWith and afterWith are
combinators that turn func1ons into
decorators that compose behaviour
© 2016 Reginald Braithwaite. Some rights reserved. 65
© 2016 Reginald Braithwaite. Some rights reserved. 66
© 2016 Reginald Braithwaite. Some rights reserved. 67
JavaScript invoca-ons are coloured
© 2016 Reginald Braithwaite. Some rights reserved. 68
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. 69
Why coloured decorators ma0er
© 2016 Reginald Braithwaite. Some rights reserved. 70
bread, revisited
class Bread {
constructor (...ingredients) {
this.ingredients = ingredients;
}
mix () {
console.log('mixing', ...this.ingredients)
};
bake () {
console.log('baking');
}
cool () {
console.log('cooling');
}
make () {
this.mix();
this.bake();
this.cool();
}
}
© 2016 Reginald Braithwaite. Some rights reserved. 71
bread, revisited
class Bread {
// ...
make () {
this.mix();
this.bake();
this.cool();
}
}
© 2016 Reginald Braithwaite. Some rights reserved. 72
Classes can be decorated too
© 2016 Reginald Braithwaite. Some rights reserved. 73
beforeAll
const beforeAll = (behaviour, ...methodNames) =>
(clazz) => {
for (let methodName of methodNames) {
const method = clazz.prototype[methodName];
Object.defineProperty(clazz.prototype, property, {
value: function (...args) {
behaviour.apply(this, args);
return method.apply(this, args);
},
writable: true
});
}
return clazz;
};
© 2016 Reginald Braithwaite. Some rights reserved. 74
afterAll
const afterAll = (behaviour, ...methodNames) =>
(clazz) => {
for (let methodName of methodNames) {
const method = clazz.prototype[methodName];
Object.defineProperty(clazz.prototype, property, {
value: function (...args) {
const returnValue = method.apply(this, args);
behaviour.apply(this, args);
return returnValue;
},
writable: true
});
}
return clazz;
};
© 2016 Reginald Braithwaite. Some rights reserved. 75
be#er bread
let invoke = (methodName) => function (...args) {
return this[methodName](...args);
}
let Bread = beforeAll(invoke('mix'), 'make')(
afterAll(invoke('cool'), 'make')(class {
// ...
make () {
this.bake();
}
})
);
© 2016 Reginald Braithwaite. Some rights reserved. 76
© 2016 Reginald Braithwaite. Some rights reserved. 77
Looking Forward
© 2016 Reginald Braithwaite. Some rights reserved. 78
ES.who-knows-when
© 2016 Reginald Braithwaite. Some rights reserved. 79
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 Bread {
// ...
make () {
this.bake();
}
}
© 2016 Reginald Braithwaite. Some rights reserved. 80
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. 81
be#er bread
class Bread {
// ...
@invokeBefore('mix')
@invokeAfter('cool')
make () {
this.bake();
}
}
© 2016 Reginald Braithwaite. Some rights reserved. 82
© 2016 Reginald Braithwaite. Some rights reserved. 83
What have we seen so far?
© 2016 Reginald Braithwaite. Some rights reserved. 84
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. 85
What have we seen so far?
• Simple composi,on
• Composi,on decorators
• Class decorators
• Method decorators
© 2016 Reginald Braithwaite. Some rights reserved. 86
Don't worry about the details!
© 2016 Reginald Braithwaite. Some rights reserved. 87
© 2016 Reginald Braithwaite. Some rights reserved. 88
it's all the same idea
Decomposi)on makes
responsibili)es explicit
© 2016 Reginald Braithwaite. Some rights reserved. 89
© 2016 Reginald Braithwaite. Some rights reserved. 90
and it's all the same idea
Composi'on makes rela'onships
explicit
© 2016 Reginald Braithwaite. Some rights reserved. 91
These ideas ma*er
© 2016 Reginald Braithwaite. Some rights reserved. 92
There are only two hard problems in
Computer Science: Cache
invalida9on, and naming things.
© 2016 Reginald Braithwaite. Some rights reserved. 93
© 2016 Reginald Braithwaite. Some rights reserved. 94
Naming en))es is hard because you
have to figure out which en))es
need to be named
© 2016 Reginald Braithwaite. Some rights reserved. 95
© 2016 Reginald Braithwaite. Some rights reserved. 96
Naming rela+onships is hard
because you have to figure out
which rela+onships need to be
named
© 2016 Reginald Braithwaite. Some rights reserved. 97
© 2016 Reginald Braithwaite. Some rights reserved. 98
Combinators do not make naming
easy
© 2016 Reginald Braithwaite. Some rights reserved. 99
© 2016 Reginald Braithwaite. Some rights reserved. 100
Combinators give us a language for
naming things
© 2016 Reginald Braithwaite. Some rights reserved. 101
© 2016 Reginald Braithwaite. Some rights reserved. 102
Do not follow
in the footsteps of the sages.
© 2016 Reginald Braithwaite. Some rights reserved. 103
Seek what they sought.
© 2016 Reginald Braithwaite. Some rights reserved. 104
Reg Braithwaite
PagerDuty, Inc.
raganwald.com
@raganwald
© 2016 Reginald Braithwaite. Some rights reserved. 105

Javascript Combinators (2016)

  • 1.
    © 2016 ReginaldBraithwaite. Some rights reserved. 1
  • 2.
    JavaScript Combinators (2016) ©2016 Reginald Braithwaite. Some rights reserved. 2
  • 3.
    © 2016 ReginaldBraithwaite. Some rights reserved. 3
  • 4.
    we'll talk about Usingcombinators for decomposi2on and composi2on © 2016 Reginald Braithwaite. Some rights reserved. 4
  • 5.
    © 2016 ReginaldBraithwaite. Some rights reserved. 5
  • 6.
    and we'll thinkabout Making responsibili/es and rela/onships explicit © 2016 Reginald Braithwaite. Some rights reserved. 6
  • 7.
    © 2016 ReginaldBraithwaite. Some rights reserved. 7
  • 8.
    © 2016 ReginaldBraithwaite. Some rights reserved. 8
  • 9.
    Decomposi)on © 2016 ReginaldBraithwaite. Some rights reserved. 9
  • 10.
    We decompose en%%esto make discreet responsibili%es explicit © 2016 Reginald Braithwaite. Some rights reserved. 10
  • 11.
    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
  • 12.
    decomposi)on by extrac)ngfunc)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
  • 13.
    © 2016 ReginaldBraithwaite. Some rights reserved. 13
  • 14.
    Composi'on © 2016 ReginaldBraithwaite. Some rights reserved. 14
  • 15.
    We compose en%%esto make the rela%onships between them explicit © 2016 Reginald Braithwaite. Some rights reserved. 15
  • 16.
    promises explicitly composeasynchronous 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
  • 17.
    © 2016 ReginaldBraithwaite. Some rights reserved. 17
  • 18.
    Decomposi)on is abouten))es © 2016 Reginald Braithwaite. Some rights reserved. 18
  • 19.
    Composi'on is aboutrela'onships © 2016 Reginald Braithwaite. Some rights reserved. 19
  • 20.
    Back to decomposi.on ©2016 Reginald Braithwaite. Some rights reserved. 20
  • 21.
    extrac'ng named func'ons Themost obvious form of decomposi2on © 2016 Reginald Braithwaite. Some rights reserved. 21
  • 22.
    © 2016 ReginaldBraithwaite. Some rights reserved. 22
  • 23.
    Extrac'ng func'ons worksfrom the inside-out © 2016 Reginald Braithwaite. Some rights reserved. 23
  • 24.
    extrac'ng named func'ons Decomposi)onof Implementa)on © 2016 Reginald Braithwaite. Some rights reserved. 24
  • 25.
    Let's look atsomething else © 2016 Reginald Braithwaite. Some rights reserved. 25
  • 26.
    © 2016 ReginaldBraithwaite. Some rights reserved. 26
  • 27.
    pluck: "A convenientversion of what is perhaps the most common use-case for map, extrac8ng a list of property values." © 2016 Reginald Braithwaite. Some rights reserved. 27
  • 28.
    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
  • 29.
    func%ons have interfaces pluck'sinterface has two parts: The collec0on, and the property © 2016 Reginald Braithwaite. Some rights reserved. 29
  • 30.
    manually decomposing pluck'sinterface let pluckFrom = (collection) => (property) => pluck(collection, property); var deStijl = [ {name: 'Theo van Doesburg', occupation: 'theorist'}, {name: 'Piet Mondriaan', occupation: 'painter'}, {name: 'Gerrit Rietveld', occupation: 'architect'}]; pluckFrom(deStijl)('name') //=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"] © 2016 Reginald Braithwaite. Some rights reserved. 30
  • 31.
    manually decomposing pluck'sinterface let pluckWith = (property) => (collection) => pluck(collection, property); var deStijl = [ {name: 'Theo van Doesburg', occupation: 'theorist'}, {name: 'Piet Mondriaan', occupation: 'painter'}, {name: 'Gerrit Rietveld', occupation: 'architect'}]; pluckFrom(deStijl)('name') //=> ["Theo van Doesburg", "Piet Mondriaan", "Gerrit Rietveld"] © 2016 Reginald Braithwaite. Some rights reserved. 31
  • 32.
    pluckFrom and pluckWith par'allyapply pluck © 2016 Reginald Braithwaite. Some rights reserved. 32
  • 33.
    © 2016 ReginaldBraithwaite. Some rights reserved. 33
  • 34.
    Par$al applica$on decomposes func$onsfrom the outside-in © 2016 Reginald Braithwaite. Some rights reserved. 34
  • 35.
    par$al applica$on Decomposi)on ofInterface © 2016 Reginald Braithwaite. Some rights reserved. 35
  • 36.
    © 2016 ReginaldBraithwaite. Some rights reserved. 36
  • 37.
    Decorators © 2016 ReginaldBraithwaite. Some rights reserved. 37
  • 38.
    A decorator isa 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
  • 39.
    par$al applica$on decomposesa 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
  • 40.
    extract closed-over binding letpluckFrom = (collection) => (property) => pluck(collection, property); // becomes: let leftApply = (fn, a) => (b) => fn(a, b); let pluckFrom = (collection) => leftApply(pluck, collection); © 2016 Reginald Braithwaite. Some rights reserved. 40
  • 41.
    extract closed-over binding Closedover bindings can be extracted, just like parameters © 2016 Reginald Braithwaite. Some rights reserved. 41
  • 42.
    extract closed-over binding DecomposingpluckFrom into leftApply gives us a decorator © 2016 Reginald Braithwaite. Some rights reserved. 42
  • 43.
    leftApply is adecorator that decomposes a func1on's interface © 2016 Reginald Braithwaite. Some rights reserved. 43
  • 44.
    © 2016 ReginaldBraithwaite. Some rights reserved. 44
  • 45.
    hmmmm What does leftApply(leftApply, leftApply) do? ©2016 Reginald Braithwaite. Some rights reserved. 45
  • 46.
    back to par*alapplica*on let rightApply = (fn, b) => (a) => fn(a, b); let pluckWith = (property) => rightApply(pluck, property); © 2016 Reginald Braithwaite. Some rights reserved. 46
  • 47.
    more decomposi+on withpar+al applica+on let get = (object, property) => object[property]; get({name: 'Gerrit Rietveld'}, 'name') //=> Gerrit Rietveld let getWith = (property) => rightApply(get, property); let nameOf = getWith('name'); nameOf({name: 'Gerrit Rietveld'}) //=> Gerrit Rietveld © 2016 Reginald Braithwaite. Some rights reserved. 47
  • 48.
    more decomposi+on withpar+al applica+on let map = (collection, fn) => collection.map(fn); let mapWith = (fn) => rightApply(map, property); © 2016 Reginald Braithwaite. Some rights reserved. 48
  • 49.
    Back to composi,on ©2016 Reginald Braithwaite. Some rights reserved. 49
  • 50.
    © 2016 ReginaldBraithwaite. Some rights reserved. 50
  • 51.
    Simple Composi+on © 2016Reginald Braithwaite. Some rights reserved. 51
  • 52.
    let compose =(a, b) => (c) => a(b(c)); © 2016 Reginald Braithwaite. Some rights reserved. 52
  • 53.
    compose in ac&on vardeStijl = [ {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. 53
  • 54.
    pluckWith = compose(mapWith, getWith); composewires the output of one func/on into the input of another © 2016 Reginald Braithwaite. Some rights reserved. 54
  • 55.
    © 2016 ReginaldBraithwaite. Some rights reserved. 55
  • 56.
    reminder: We compose en**esto make the rela*onships between them explicit © 2016 Reginald Braithwaite. Some rights reserved. 56
  • 57.
    More Composi+on © 2016Reginald Braithwaite. Some rights reserved. 57
  • 58.
    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. 58
  • 59.
    composi'on with before letbefore = (fn, decoration) => (...args) => { decoration(...args); return fn(...args); }; let bakeBread = before(bake, mix); let makeBread = (...ingredients) => { bakeBread(); cool(); } © 2016 Reginald Braithwaite. Some rights reserved. 59
  • 60.
    before makes the)me rela)onship between two func)ons explicit © 2016 Reginald Braithwaite. Some rights reserved. 60
  • 61.
    composi'on with after letafter = (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. 61
  • 62.
    after also makesthe +me rela+onship between two func+ons explicit © 2016 Reginald Braithwaite. Some rights reserved. 62
  • 63.
    decomposing before let beforeWith= (decoration) => rightApply(before, decoration); let mixBefore = beforeWith(mix); let bakeBread = mixBefore(bake); © 2016 Reginald Braithwaite. Some rights reserved. 63
  • 64.
    decomposing after let afterWith= (decoration) => rightApply(after, decoration); let coolAfter = afterWith(after); let makeBread = coolAfter(bakeBread); © 2016 Reginald Braithwaite. Some rights reserved. 64
  • 65.
    beforeWith and afterWithare combinators that turn func1ons into decorators that compose behaviour © 2016 Reginald Braithwaite. Some rights reserved. 65
  • 66.
    © 2016 ReginaldBraithwaite. Some rights reserved. 66
  • 67.
    © 2016 ReginaldBraithwaite. Some rights reserved. 67
  • 68.
    JavaScript invoca-ons arecoloured © 2016 Reginald Braithwaite. Some rights reserved. 68
  • 69.
    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. 69
  • 70.
    Why coloured decoratorsma0er © 2016 Reginald Braithwaite. Some rights reserved. 70
  • 71.
    bread, revisited class Bread{ constructor (...ingredients) { this.ingredients = ingredients; } mix () { console.log('mixing', ...this.ingredients) }; bake () { console.log('baking'); } cool () { console.log('cooling'); } make () { this.mix(); this.bake(); this.cool(); } } © 2016 Reginald Braithwaite. Some rights reserved. 71
  • 72.
    bread, revisited class Bread{ // ... make () { this.mix(); this.bake(); this.cool(); } } © 2016 Reginald Braithwaite. Some rights reserved. 72
  • 73.
    Classes can bedecorated too © 2016 Reginald Braithwaite. Some rights reserved. 73
  • 74.
    beforeAll const beforeAll =(behaviour, ...methodNames) => (clazz) => { for (let methodName of methodNames) { const method = clazz.prototype[methodName]; Object.defineProperty(clazz.prototype, property, { value: function (...args) { behaviour.apply(this, args); return method.apply(this, args); }, writable: true }); } return clazz; }; © 2016 Reginald Braithwaite. Some rights reserved. 74
  • 75.
    afterAll const afterAll =(behaviour, ...methodNames) => (clazz) => { for (let methodName of methodNames) { const method = clazz.prototype[methodName]; Object.defineProperty(clazz.prototype, property, { value: function (...args) { const returnValue = method.apply(this, args); behaviour.apply(this, args); return returnValue; }, writable: true }); } return clazz; }; © 2016 Reginald Braithwaite. Some rights reserved. 75
  • 76.
    be#er bread let invoke= (methodName) => function (...args) { return this[methodName](...args); } let Bread = beforeAll(invoke('mix'), 'make')( afterAll(invoke('cool'), 'make')(class { // ... make () { this.bake(); } }) ); © 2016 Reginald Braithwaite. Some rights reserved. 76
  • 77.
    © 2016 ReginaldBraithwaite. Some rights reserved. 77
  • 78.
    Looking Forward © 2016Reginald Braithwaite. Some rights reserved. 78
  • 79.
    ES.who-knows-when © 2016 ReginaldBraithwaite. Some rights reserved. 79
  • 80.
    be#er bread withclass decorator sugar let invoke = (methodName) => function (...args) { return this[methodName](...args); } @beforeAll(invoke('mix'), 'make') @afterAll(invoke('cool'), 'make') class Bread { // ... make () { this.bake(); } } © 2016 Reginald Braithwaite. Some rights reserved. 80
  • 81.
    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. 81
  • 82.
    be#er bread class Bread{ // ... @invokeBefore('mix') @invokeAfter('cool') make () { this.bake(); } } © 2016 Reginald Braithwaite. Some rights reserved. 82
  • 83.
    © 2016 ReginaldBraithwaite. Some rights reserved. 83
  • 84.
    What have weseen so far? © 2016 Reginald Braithwaite. Some rights reserved. 84
  • 85.
    What have weseen so far? • Extract func,on • Promise interface • Par,al applica,on • Extract closed-over binding (more!) © 2016 Reginald Braithwaite. Some rights reserved. 85
  • 86.
    What have weseen so far? • Simple composi,on • Composi,on decorators • Class decorators • Method decorators © 2016 Reginald Braithwaite. Some rights reserved. 86
  • 87.
    Don't worry aboutthe details! © 2016 Reginald Braithwaite. Some rights reserved. 87
  • 88.
    © 2016 ReginaldBraithwaite. Some rights reserved. 88
  • 89.
    it's all thesame idea Decomposi)on makes responsibili)es explicit © 2016 Reginald Braithwaite. Some rights reserved. 89
  • 90.
    © 2016 ReginaldBraithwaite. Some rights reserved. 90
  • 91.
    and it's allthe same idea Composi'on makes rela'onships explicit © 2016 Reginald Braithwaite. Some rights reserved. 91
  • 92.
    These ideas ma*er ©2016 Reginald Braithwaite. Some rights reserved. 92
  • 93.
    There are onlytwo hard problems in Computer Science: Cache invalida9on, and naming things. © 2016 Reginald Braithwaite. Some rights reserved. 93
  • 94.
    © 2016 ReginaldBraithwaite. Some rights reserved. 94
  • 95.
    Naming en))es ishard because you have to figure out which en))es need to be named © 2016 Reginald Braithwaite. Some rights reserved. 95
  • 96.
    © 2016 ReginaldBraithwaite. Some rights reserved. 96
  • 97.
    Naming rela+onships ishard because you have to figure out which rela+onships need to be named © 2016 Reginald Braithwaite. Some rights reserved. 97
  • 98.
    © 2016 ReginaldBraithwaite. Some rights reserved. 98
  • 99.
    Combinators do notmake naming easy © 2016 Reginald Braithwaite. Some rights reserved. 99
  • 100.
    © 2016 ReginaldBraithwaite. Some rights reserved. 100
  • 101.
    Combinators give usa language for naming things © 2016 Reginald Braithwaite. Some rights reserved. 101
  • 102.
    © 2016 ReginaldBraithwaite. Some rights reserved. 102
  • 103.
    Do not follow inthe footsteps of the sages. © 2016 Reginald Braithwaite. Some rights reserved. 103
  • 104.
    Seek what theysought. © 2016 Reginald Braithwaite. Some rights reserved. 104
  • 105.
    Reg Braithwaite PagerDuty, Inc. raganwald.com @raganwald ©2016 Reginald Braithwaite. Some rights reserved. 105