javascript combinators, the “six” edition

108
© 2016 Reginald Braithwaite. Some rights reserved. 1

Upload: reginald-braithwaite

Post on 16-Apr-2017

215 views

Category:

Technology


2 download

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/