Slide 1

Slide 1 text

© 2016 Reginald Braithwaite. Some rights reserved. 1

Slide 2

Slide 2 text

JavaScript Combinators the "six" edi*on © 2016 Reginald Braithwaite. Some rights reserved. 2

Slide 3

Slide 3 text

© 2016 Reginald Braithwaite. Some rights reserved. 3

Slide 4

Slide 4 text

we'll talk about Using combinators for decomposi2on and composi2on © 2016 Reginald Braithwaite. Some rights reserved. 4

Slide 5

Slide 5 text

© 2016 Reginald Braithwaite. Some rights reserved. 5

Slide 6

Slide 6 text

and we'll think about Making responsibili/es and rela/onships explicit © 2016 Reginald Braithwaite. Some rights reserved. 6

Slide 7

Slide 7 text

© 2016 Reginald Braithwaite. Some rights reserved. 7

Slide 8

Slide 8 text

© 2016 Reginald Braithwaite. Some rights reserved. 8

Slide 9

Slide 9 text

Decomposi)on © 2016 Reginald Braithwaite. Some rights reserved. 9

Slide 10

Slide 10 text

We decompose en%%es to make discreet responsibili%es explicit © 2016 Reginald Braithwaite. Some rights reserved. 10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

© 2016 Reginald Braithwaite. Some rights reserved. 13

Slide 14

Slide 14 text

Composi'on © 2016 Reginald Braithwaite. Some rights reserved. 14

Slide 15

Slide 15 text

We compose en%%es to make the rela%onships between them explicit © 2016 Reginald Braithwaite. Some rights reserved. 15

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

© 2016 Reginald Braithwaite. Some rights reserved. 17

Slide 18

Slide 18 text

Decomposi)on is about en))es © 2016 Reginald Braithwaite. Some rights reserved. 18

Slide 19

Slide 19 text

Composi'on is about rela'onships © 2016 Reginald Braithwaite. Some rights reserved. 19

Slide 20

Slide 20 text

Back to decomposi.on © 2016 Reginald Braithwaite. Some rights reserved. 20

Slide 21

Slide 21 text

extrac'ng named func'ons The most obvious form of decomposi2on © 2016 Reginald Braithwaite. Some rights reserved. 21

Slide 22

Slide 22 text

© 2016 Reginald Braithwaite. Some rights reserved. 22

Slide 23

Slide 23 text

Extrac'ng func'ons works from the inside-out © 2016 Reginald Braithwaite. Some rights reserved. 23

Slide 24

Slide 24 text

extrac'ng named func'ons Decomposi)on of Implementa)on © 2016 Reginald Braithwaite. Some rights reserved. 24

Slide 25

Slide 25 text

Let's look at something else © 2016 Reginald Braithwaite. Some rights reserved. 25

Slide 26

Slide 26 text

© 2016 Reginald Braithwaite. Some rights reserved. 26

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

func%ons have interfaces pluck's interface has two parts: The collec0on, and the property © 2016 Reginald Braithwaite. Some rights reserved. 29

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

pluckFrom and pluckWith par'ally apply pluck © 2016 Reginald Braithwaite. Some rights reserved. 32

Slide 33

Slide 33 text

© 2016 Reginald Braithwaite. Some rights reserved. 33

Slide 34

Slide 34 text

Par$al applica$on decomposes func$ons from the outside-in © 2016 Reginald Braithwaite. Some rights reserved. 34

Slide 35

Slide 35 text

par$al applica$on Decomposi)on of Interface © 2016 Reginald Braithwaite. Some rights reserved. 35

Slide 36

Slide 36 text

© 2016 Reginald Braithwaite. Some rights reserved. 36

Slide 37

Slide 37 text

Decorators © 2016 Reginald Braithwaite. Some rights reserved. 37

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

© 2016 Reginald Braithwaite. Some rights reserved. 42

Slide 43

Slide 43 text

hmmmm What is leftApply(leftApply, leftApply)? © 2016 Reginald Braithwaite. Some rights reserved. 43

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

© 2016 Reginald Braithwaite. Some rights reserved. 47

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

last one let map = (collection, fn) => collection.map(fn); let mapWith = C(map); let namesOf = mapWith(nameOf); © 2016 Reginald Braithwaite. Some rights reserved. 49

Slide 50

Slide 50 text

© 2016 Reginald Braithwaite. Some rights reserved. 50

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Back to composi,on © 2016 Reginald Braithwaite. Some rights reserved. 52

Slide 53

Slide 53 text

© 2016 Reginald Braithwaite. Some rights reserved. 53

Slide 54

Slide 54 text

Simple Composi+on © 2016 Reginald Braithwaite. Some rights reserved. 54

Slide 55

Slide 55 text

let compose = (a, b) => (c) => a(b(c)); © 2016 Reginald Braithwaite. Some rights reserved. 55

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

pluckWith = compose(mapWith, getWith); © 2016 Reginald Braithwaite. Some rights reserved. 57

Slide 58

Slide 58 text

© 2016 Reginald Braithwaite. Some rights reserved. 58

Slide 59

Slide 59 text

reminder: We compose en**es to make the rela*onships between them explicit © 2016 Reginald Braithwaite. Some rights reserved. 59

Slide 60

Slide 60 text

More Composi+on © 2016 Reginald Braithwaite. Some rights reserved. 60

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

before makes the )me rela)onship between two func)ons explicit © 2016 Reginald Braithwaite. Some rights reserved. 63

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

after also makes the +me rela+onship between two func+ons explicit © 2016 Reginald Braithwaite. Some rights reserved. 65

Slide 66

Slide 66 text

decomposing before let beforeWith = (decoration) => rightApply(before, decoration); let mixBefore = beforeWith(mix); let bakeBread = mixBefore(bake); © 2016 Reginald Braithwaite. Some rights reserved. 66

Slide 67

Slide 67 text

decomposing after let afterWith = (decoration) => rightApply(after, decoration); let coolAfter = afterWith(after); let makeBread = coolAfter(bakeBread); © 2016 Reginald Braithwaite. Some rights reserved. 67

Slide 68

Slide 68 text

beforeWith and afterWith are combinators that turn func1ons into decorators that compose behaviour © 2016 Reginald Braithwaite. Some rights reserved. 68

Slide 69

Slide 69 text

© 2016 Reginald Braithwaite. Some rights reserved. 69

Slide 70

Slide 70 text

© 2016 Reginald Braithwaite. Some rights reserved. 70

Slide 71

Slide 71 text

JavaScript invoca-ons are coloured © 2016 Reginald Braithwaite. Some rights reserved. 71

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Why coloured decorators ma0er © 2016 Reginald Braithwaite. Some rights reserved. 73

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

bread, revisited class Bread { // ... make () { this.mix(); this.bake(); this.cool(); } } © 2016 Reginald Braithwaite. Some rights reserved. 75

Slide 76

Slide 76 text

Classes can be decorated too © 2016 Reginald Braithwaite. Some rights reserved. 76

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

© 2016 Reginald Braithwaite. Some rights reserved. 80

Slide 81

Slide 81 text

Looking Forward © 2016 Reginald Braithwaite. Some rights reserved. 81

Slide 82

Slide 82 text

ES.who-knows-when © 2016 Reginald Braithwaite. Some rights reserved. 82

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

be#er make class Bread { // ... @invokeBefore('mix') @invokeAfter('cool') make () { this.bake(); } } © 2016 Reginald Braithwaite. Some rights reserved. 85

Slide 86

Slide 86 text

© 2016 Reginald Braithwaite. Some rights reserved. 86

Slide 87

Slide 87 text

What have we seen so far? © 2016 Reginald Braithwaite. Some rights reserved. 87

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

What have we seen so far? • Simple composi,on • Composi,on decorators • Class decorators • Method decorators © 2016 Reginald Braithwaite. Some rights reserved. 89

Slide 90

Slide 90 text

Don't worry about the details! © 2016 Reginald Braithwaite. Some rights reserved. 90

Slide 91

Slide 91 text

© 2016 Reginald Braithwaite. Some rights reserved. 91

Slide 92

Slide 92 text

it's all the same idea Decomposi)on makes responsibili)es explicit © 2016 Reginald Braithwaite. Some rights reserved. 92

Slide 93

Slide 93 text

© 2016 Reginald Braithwaite. Some rights reserved. 93

Slide 94

Slide 94 text

and it's all the same idea Composi'on makes rela'onships explicit © 2016 Reginald Braithwaite. Some rights reserved. 94

Slide 95

Slide 95 text

These ideas ma*er © 2016 Reginald Braithwaite. Some rights reserved. 95

Slide 96

Slide 96 text

There are only two hard problems in Computer Science: Cache invalida9on, and naming things. © 2016 Reginald Braithwaite. Some rights reserved. 96

Slide 97

Slide 97 text

© 2016 Reginald Braithwaite. Some rights reserved. 97

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

© 2016 Reginald Braithwaite. Some rights reserved. 99

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

© 2016 Reginald Braithwaite. Some rights reserved. 101

Slide 102

Slide 102 text

Combinators do not make naming easy © 2016 Reginald Braithwaite. Some rights reserved. 102

Slide 103

Slide 103 text

© 2016 Reginald Braithwaite. Some rights reserved. 103

Slide 104

Slide 104 text

Combinators give us a language for naming things in code © 2016 Reginald Braithwaite. Some rights reserved. 104

Slide 105

Slide 105 text

© 2016 Reginald Braithwaite. Some rights reserved. 105

Slide 106

Slide 106 text

Do not follow in the footsteps of the sages. © 2016 Reginald Braithwaite. Some rights reserved. 106

Slide 107

Slide 107 text

Seek what they sought. © 2016 Reginald Braithwaite. Some rights reserved. 107

Slide 108

Slide 108 text

Reg Braithwaite PagerDuty, Inc. raganwald.com @raganwald © 2016 Reginald Braithwaite. Some rights reserved. 108