Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
JavaScript Combinators, the “six” edition
Search
Reg Braithwaite
January 13, 2016
Technology
8
1.3k
JavaScript Combinators, the “six” edition
Presented at NDC London, January 13, 2016
Reg Braithwaite
January 13, 2016
Tweet
Share
More Decks by Reg Braithwaite
See All by Reg Braithwaite
Courage
raganwald
0
100
Waste in Software Development
raganwald
0
160
First-Class Commands, the 2017 Edition
raganwald
1
160
Optimism and the Growth Mindset
raganwald
0
260
ember-concurrency: an experience report
raganwald
1
120
Optimism II
raganwald
0
370
Optimism
raganwald
0
1.9k
First-Class Commands: an unexpectedly fertile design pattern
raganwald
4
2.7k
Duck Typing, Compatibility, and the Adaptor Pattern
raganwald
7
10k
Other Decks in Technology
See All in Technology
ISUCONに強くなるかもしれない日々の過ごしかた/Findy ISUCON 2024-11-14
fujiwara3
8
880
AWS Lambda のトラブルシュートをしていて思うこと
kazzpapa3
2
190
B2B SaaSから見た最近のC#/.NETの進化
sansantech
PRO
0
920
これまでの計測・開発・デプロイ方法全部見せます! / Findy ISUCON 2024-11-14
tohutohu
3
370
iOS/Androidで同じUI体験をネ イティブで作成する際に気をつ けたい落とし穴
fumiyasac0921
1
110
アジャイルチームがらしさを発揮するための目標づくり / Making the goal and enabling the team
kakehashi
3
150
VideoMamba: State Space Model for Efficient Video Understanding
chou500
0
190
Security-JAWS【第35回】勉強会クラウドにおけるマルウェアやコンテンツ改ざんへの対策
4su_para
0
180
Terraform Stacks入門 #HashiTalks
msato
0
360
SSMRunbook作成の勘所_20241120
koichiotomo
3
160
マルチプロダクトな開発組織で 「開発生産性」に向き合うために試みたこと / Improving Multi-Product Dev Productivity
sugamasao
1
310
日経電子版のStoreKit2フルリニューアル
shimastripe
1
150
Featured
See All Featured
Imperfection Machines: The Place of Print at Facebook
scottboms
265
13k
Designing the Hi-DPI Web
ddemaree
280
34k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
6
430
A Modern Web Designer's Workflow
chriscoyier
693
190k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
Speed Design
sergeychernyshev
25
620
GitHub's CSS Performance
jonrohan
1030
460k
Designing on Purpose - Digital PM Summit 2013
jponch
115
7k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
229
52k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
28
8.2k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
Transcript
© 2016 Reginald Braithwaite. Some rights reserved. 1
JavaScript Combinators the "six" edi*on © 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 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
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
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); // 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
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
© 2016 Reginald Braithwaite. Some rights reserved. 42
hmmmm What is leftApply(leftApply, leftApply)? © 2016 Reginald Braithwaite. Some
rights reserved. 43
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
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
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
© 2016 Reginald Braithwaite. Some rights reserved. 47
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
last one let map = (collection, fn) => collection.map(fn); let
mapWith = C(map); let namesOf = mapWith(nameOf); © 2016 Reginald Braithwaite. Some rights reserved. 49
© 2016 Reginald Braithwaite. Some rights reserved. 50
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
Back to composi,on © 2016 Reginald Braithwaite. Some rights reserved.
52
© 2016 Reginald Braithwaite. Some rights reserved. 53
Simple Composi+on © 2016 Reginald Braithwaite. Some rights reserved. 54
let compose = (a, b) => (c) => a(b(c)); ©
2016 Reginald Braithwaite. Some rights reserved. 55
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
pluckWith = compose(mapWith, getWith); © 2016 Reginald Braithwaite. Some rights
reserved. 57
© 2016 Reginald Braithwaite. Some rights reserved. 58
reminder: We compose en**es to make the rela*onships between them
explicit © 2016 Reginald Braithwaite. Some rights reserved. 59
More Composi+on © 2016 Reginald Braithwaite. Some rights reserved. 60
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
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
before makes the )me rela)onship between two func)ons explicit ©
2016 Reginald Braithwaite. Some rights reserved. 63
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
after also makes the +me rela+onship between two func+ons explicit
© 2016 Reginald Braithwaite. Some rights reserved. 65
decomposing before let beforeWith = (decoration) => rightApply(before, decoration); let
mixBefore = beforeWith(mix); let bakeBread = mixBefore(bake); © 2016 Reginald Braithwaite. Some rights reserved. 66
decomposing after let afterWith = (decoration) => rightApply(after, decoration); let
coolAfter = afterWith(after); let makeBread = coolAfter(bakeBread); © 2016 Reginald Braithwaite. Some rights reserved. 67
beforeWith and afterWith are combinators that turn func1ons into decorators
that compose behaviour © 2016 Reginald Braithwaite. Some rights reserved. 68
© 2016 Reginald Braithwaite. Some rights reserved. 69
© 2016 Reginald Braithwaite. Some rights reserved. 70
JavaScript invoca-ons are coloured © 2016 Reginald Braithwaite. Some rights
reserved. 71
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
Why coloured decorators ma0er © 2016 Reginald Braithwaite. Some rights
reserved. 73
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
bread, revisited class Bread { // ... make () {
this.mix(); this.bake(); this.cool(); } } © 2016 Reginald Braithwaite. Some rights reserved. 75
Classes can be decorated too © 2016 Reginald Braithwaite. Some
rights reserved. 76
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
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
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
© 2016 Reginald Braithwaite. Some rights reserved. 80
Looking Forward © 2016 Reginald Braithwaite. Some rights reserved. 81
ES.who-knows-when © 2016 Reginald Braithwaite. Some rights reserved. 82
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
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
be#er make class Bread { // ... @invokeBefore('mix') @invokeAfter('cool') make
() { this.bake(); } } © 2016 Reginald Braithwaite. Some rights reserved. 85
© 2016 Reginald Braithwaite. Some rights reserved. 86
What have we seen so far? © 2016 Reginald Braithwaite.
Some rights reserved. 87
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
What have we seen so far? • Simple composi,on •
Composi,on decorators • Class decorators • Method decorators © 2016 Reginald Braithwaite. Some rights reserved. 89
Don't worry about the details! © 2016 Reginald Braithwaite. Some
rights reserved. 90
© 2016 Reginald Braithwaite. Some rights reserved. 91
it's all the same idea Decomposi)on makes responsibili)es explicit ©
2016 Reginald Braithwaite. Some rights reserved. 92
© 2016 Reginald Braithwaite. Some rights reserved. 93
and it's all the same idea Composi'on makes rela'onships explicit
© 2016 Reginald Braithwaite. Some rights reserved. 94
These ideas ma*er © 2016 Reginald Braithwaite. Some rights reserved.
95
There are only two hard problems in Computer Science: Cache
invalida9on, and naming things. © 2016 Reginald Braithwaite. Some rights reserved. 96
© 2016 Reginald Braithwaite. Some rights reserved. 97
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
© 2016 Reginald Braithwaite. Some rights reserved. 99
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
© 2016 Reginald Braithwaite. Some rights reserved. 101
Combinators do not make naming easy © 2016 Reginald Braithwaite.
Some rights reserved. 102
© 2016 Reginald Braithwaite. Some rights reserved. 103
Combinators give us a language for naming things in code
© 2016 Reginald Braithwaite. Some rights reserved. 104
© 2016 Reginald Braithwaite. Some rights reserved. 105
Do not follow in the footsteps of the sages. ©
2016 Reginald Braithwaite. Some rights reserved. 106
Seek what they sought. © 2016 Reginald Braithwaite. Some rights
reserved. 107
Reg Braithwaite PagerDuty, Inc. raganwald.com @raganwald © 2016 Reginald Braithwaite.
Some rights reserved. 108