Slide 1

Slide 1 text

© 2016 Reginald Braithwaite. Some rights reserved. 1

Slide 2

Slide 2 text

First-Class Commands an unexpectedly fer/le design pa3ern © 2016 Reginald Braithwaite. Some rights reserved. 2

Slide 3

Slide 3 text

why do we care about commands? © 2016 Reginald Braithwaite. Some rights reserved. 3

Slide 4

Slide 4 text

© 2016 Reginald Braithwaite. Some rights reserved. 4

Slide 5

Slide 5 text

the canonical example: mutable data © 2016 Reginald Braithwaite. Some rights reserved. 5

Slide 6

Slide 6 text

class Buffer { constructor (text = '') { this.text = text; } replaceWith (replacement, from = 0, to = this.text.length) { this.text = this.text.slice(0, from) + replacement + this.text.slice(to); return this; } toString () { return this.text; } } © 2016 Reginald Braithwaite. Some rights reserved. 6

Slide 7

Slide 7 text

let buffer = new Buffer(); buffer.replaceWith( "The quick brown fox jumped over the lazy dog" ); buffer.replaceWith("fast", 4, 9); buffer.replaceWith("canine", 40, 43); //=> The fast brown fox jumped over the lazy canine © 2016 Reginald Braithwaite. Some rights reserved. 7

Slide 8

Slide 8 text

buffer is an object © 2016 Reginald Braithwaite. Some rights reserved. 8

Slide 9

Slide 9 text

we treat objects as first-class en00es © 2016 Reginald Braithwaite. Some rights reserved. 9

Slide 10

Slide 10 text

replaceWith is a method © 2016 Reginald Braithwaite. Some rights reserved. 10

Slide 11

Slide 11 text

we can treat methods as first-class en11es © 2016 Reginald Braithwaite. Some rights reserved. 11

Slide 12

Slide 12 text

buffer.replaceWith("fast", 4, 9) is an invoca)on © 2016 Reginald Braithwaite. Some rights reserved. 12

Slide 13

Slide 13 text

what does it mean to treat an invoca0on as a first-class en0ty? © 2016 Reginald Braithwaite. Some rights reserved. 13

Slide 14

Slide 14 text

store it © 2016 Reginald Braithwaite. Some rights reserved. 14

Slide 15

Slide 15 text

class Edit { constructor (buffer, {replacement, from, to}) { this.buffer = buffer; Object.assign(this, {replacement, from, to}); } doIt () { this.buffer.text = this.buffer.text.slice(0, this.from) + this.replacement + this.buffer.text.slice(this.to); return this.buffer; } } © 2016 Reginald Braithwaite. Some rights reserved. 15

Slide 16

Slide 16 text

class Buffer { constructor (text = '') { this.text = text; } replaceWith (replacement, from = 0, to = this.text.length) { return new Edit(this, {replacement, from, to}); } toString () { return this.text; } } © 2016 Reginald Braithwaite. Some rights reserved. 16

Slide 17

Slide 17 text

let buffer = new Buffer(), jobQueue = []; jobQueue.push( buffer.replaceWith( "The quick brown fox jumped over the lazy dog" ) ); jobQueue.push( buffer.replaceWith("fast", 4, 9) ); jobQueue.push( buffer.replaceWith("canine", 40, 43) ); while (jobQueue.length > 0) { jobQueue.shift().doIt(); } //=> The fast brown fox jumped over the lazy canine © 2016 Reginald Braithwaite. Some rights reserved. 17

Slide 18

Slide 18 text

query it © 2016 Reginald Braithwaite. Some rights reserved. 18

Slide 19

Slide 19 text

class Edit { netChange () { return this.from - this.to + this.replacement.length; } } © 2016 Reginald Braithwaite. Some rights reserved. 19

Slide 20

Slide 20 text

let buffer = new Buffer(); buffer.replaceWith( "The quick brown fox jumped over the lazy dog" ).netChange(); //=> 44 buffer.replaceWith("fast", 4, 9).netChange(); //=> -1 © 2016 Reginald Braithwaite. Some rights reserved. 20

Slide 21

Slide 21 text

transform it © 2016 Reginald Braithwaite. Some rights reserved. 21

Slide 22

Slide 22 text

class Edit { reversed () { let replacement = this.buffer.text.slice(this.from, this.to), from = this.from, to = from + this.replacement.length; return new Edit(buffer, {replacement, from, to}); } } © 2016 Reginald Braithwaite. Some rights reserved. 22

Slide 23

Slide 23 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); let doer = buffer.replaceWith("fast", 4, 9), undoer = doer.reversed(); doer.doIt(); //=> The fast brown fox jumped over the lazy dog undoer.doIt(); //=> The quick brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 23

Slide 24

Slide 24 text

all together now © 2016 Reginald Braithwaite. Some rights reserved. 24

Slide 25

Slide 25 text

class Buffer { constructor (text = '') { this.text = text; this.history = []; this.future = []; } } © 2016 Reginald Braithwaite. Some rights reserved. 25

Slide 26

Slide 26 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let doer = new Edit(this, {replacement, from, to}), undoer = doer.reversed(); this.history.push(undoer); this.future = []; return doer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 26

Slide 27

Slide 27 text

undo © 2016 Reginald Braithwaite. Some rights reserved. 27

Slide 28

Slide 28 text

class Buffer { undo () { let undoer = this.history.pop(), redoer = undoer.reversed(); this.future.unshift(redoer); return undoer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 28

Slide 29

Slide 29 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); buffer.replaceWith("fast", 4, 9) //=> The fast brown fox jumped over the lazy dog buffer.replaceWith("canine", 40, 43) //=> The fast brown fox jumped over the lazy canine © 2016 Reginald Braithwaite. Some rights reserved. 29

Slide 30

Slide 30 text

buffer.undo() //=> The fast brown fox jumped over the lazy dog buffer.undo() //=> The quick brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 30

Slide 31

Slide 31 text

redo © 2016 Reginald Braithwaite. Some rights reserved. 31

Slide 32

Slide 32 text

class Buffer { redo () { let redoer = this.future.shift(), undoer = redoer.reversed(); this.history.push(undoer); return redoer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 32

Slide 33

Slide 33 text

buffer.redo() //=> The fast brown fox jumped over the lazy dog buffer.redo() //=> The fast brown fox jumped over the lazy canine © 2016 Reginald Braithwaite. Some rights reserved. 33

Slide 34

Slide 34 text

that's the basic command pa0ern © 2016 Reginald Braithwaite. Some rights reserved. 34

Slide 35

Slide 35 text

invoca'ons as first-class en''es: we stored them; we queried them; we transformed them. © 2016 Reginald Braithwaite. Some rights reserved. 35

Slide 36

Slide 36 text

ques%on! © 2016 Reginald Braithwaite. Some rights reserved. 36

Slide 37

Slide 37 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let doer = new Edit(this, {replacement, from, to}), undoer = doer.reversed(); this.history.push(undoer); this.future = []; return doer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 37

Slide 38

Slide 38 text

why do we have to throw the future away? © 2016 Reginald Braithwaite. Some rights reserved. 38

Slide 39

Slide 39 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let doer = new Edit(this, {replacement, from, to}), undoer = doer.reversed(); this.history.push(undoer); // this.future = []; return doer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 39

Slide 40

Slide 40 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); buffer.replaceWith("fast", 4, 9); //=> The fast brown fox jumped over the lazy dog buffer.undo(); //=> The quick brown fox jumped over the lazy dog buffer.replaceWith("My", 0, 3); //=> My quick brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 40

Slide 41

Slide 41 text

what happens when we evaluate buffer.redo()? © 2016 Reginald Braithwaite. Some rights reserved. 41

Slide 42

Slide 42 text

"My qfastbrown fox jumped over the lazy dog" © 2016 Reginald Braithwaite. Some rights reserved. 42

Slide 43

Slide 43 text

© 2016 Reginald Braithwaite. Some rights reserved. 43

Slide 44

Slide 44 text

let's consider commands as a history © 2016 Reginald Braithwaite. Some rights reserved. 44

Slide 45

Slide 45 text

let buffer = new Buffer("The quick brown fox jumped over the lazy dog"); "The quick brown fox jumped over the lazy dog" // PAST // FUTURE © 2016 Reginald Braithwaite. Some rights reserved. 45

Slide 46

Slide 46 text

buffer.replaceWith("fast", 4, 9) "The fast brown fox jumped over the lazy dog" // PAST replaceWith("fast", 4, 9) // FUTURE © 2016 Reginald Braithwaite. Some rights reserved. 46

Slide 47

Slide 47 text

buffer.undo() "The quick brown fox jumped over the lazy dog" // PAST // FUTURE replaceWith("fast", 4, 9) © 2016 Reginald Braithwaite. Some rights reserved. 47

Slide 48

Slide 48 text

buffer.replaceWith("My", 0, 3) "My quick brown fox jumped over the lazy dog" // PAST replaceWith("My", 0, 3) // FUTURE replaceWith("fast", 4, 9) © 2016 Reginald Braithwaite. Some rights reserved. 48

Slide 49

Slide 49 text

buffer.redo() "My qfastbrown fox jumped over the lazy dog" // PAST replaceWith("My", 0, 3) replaceWith("fast", 4, 9) // FUTURE © 2016 Reginald Braithwaite. Some rights reserved. 49

Slide 50

Slide 50 text

every command depends on the history of commands preceding it © 2016 Reginald Braithwaite. Some rights reserved. 50

Slide 51

Slide 51 text

prepending a command into its history alters the command © 2016 Reginald Braithwaite. Some rights reserved. 51

Slide 52

Slide 52 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); let fast = new Edit( buffer, { replacement: "fast", from: 4, to: 9 } ); let my = new Edit( buffer, { replacement: "My", from: 0, to: 3 } ); © 2016 Reginald Braithwaite. Some rights reserved. 52

Slide 53

Slide 53 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); let fast = new Edit( buffer, { replacement: "fast", from: 4, to: 9 } ); let my = new Edit( buffer, { replacement: "My", from: 0, to: 3 } ); © 2016 Reginald Braithwaite. Some rights reserved. 53

Slide 54

Slide 54 text

class Edit { isBefore (other) { return other.from >= this.to; } } fast.isBefore(my); //=> false my.isBefore(fast); //=> true © 2016 Reginald Braithwaite. Some rights reserved. 54

Slide 55

Slide 55 text

class Edit { prependedWith (other) { if (this.isBefore(other)) { return this; } else if (other.isBefore(this)) { let change = other.netChange(), {replacement, from, to} = this; from = from + change; to = to + change; return new Edit(this.buffer, {replacement, from, to}) } } } © 2016 Reginald Braithwaite. Some rights reserved. 55

Slide 56

Slide 56 text

my.prependedWith(fast) //=> buffer.replaceWith("My", 0, 3) fast.prependedWith(my) //=> buffer.replaceWith("fast", 3, 8) © 2016 Reginald Braithwaite. Some rights reserved. 56

Slide 57

Slide 57 text

my.prependedWith(fast) //=> buffer.replaceWith("My", 0, 3) fast.prependedWith(my) //=> buffer.replaceWith("fast", 3, 8) © 2016 Reginald Braithwaite. Some rights reserved. 57

Slide 58

Slide 58 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let doer = new Edit(this, {replacement, from, to}), undoer = doer.reversed(); this.history.push(undoer); this.future = this.future.map( (edit) => edit.prependedWith(doer) ); return doer.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 58

Slide 59

Slide 59 text

let's start over © 2016 Reginald Braithwaite. Some rights reserved. 59

Slide 60

Slide 60 text

let buffer = new Buffer( "The quick brown fox jumped over the lazy dog" ); buffer.replaceWith("fast", 4, 9); //=> The fast brown fox jumped over the lazy dog buffer.undo(); //=> The quick brown fox jumped over the lazy dog buffer.replaceWith("My", 0, 3); //=> My quick brown fox jumped over the lazy dog buffer.redo(); © 2016 Reginald Braithwaite. Some rights reserved. 60

Slide 61

Slide 61 text

"My fast brown fox jumped over the lazy dog" © 2016 Reginald Braithwaite. Some rights reserved. 61

Slide 62

Slide 62 text

what did fixing redo teach us about invoca3ons as first-class en33es? © 2016 Reginald Braithwaite. Some rights reserved. 62

Slide 63

Slide 63 text

"People assume that .me is a strict progression of cause to effect, but actually from a non-linear, non- subjec.ve viewpoint—it's more like a big ball of wibbly wobbly… .me-y wimey… stuff." © 2016 Reginald Braithwaite. Some rights reserved. 63

Slide 64

Slide 64 text

reversed() and prependedWith() show us we can change both the direc1on and order of 1me. © 2016 Reginald Braithwaite. Some rights reserved. 64

Slide 65

Slide 65 text

© 2016 Reginald Braithwaite. Some rights reserved. 65

Slide 66

Slide 66 text

let alice = new Buffer( "The quick brown fox jumped over the lazy dog" ); let bob = new Buffer( "The quick brown fox jumped over the lazy dog" ); © 2016 Reginald Braithwaite. Some rights reserved. 66

Slide 67

Slide 67 text

for simplicity, we'll omit undo , redo and reversed class Buffer { constructor (text = '') { this.text = text; this.history = []; } } © 2016 Reginald Braithwaite. Some rights reserved. 67

Slide 68

Slide 68 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let edit = new Edit(this, {replacement, from, to} ); this.history.push(edit); return edit.doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 68

Slide 69

Slide 69 text

alice.replaceWith("My", 0, 3); //=> My quick brown fox jumped over the lazy dog bob.replaceWith("fast", 4, 9); //=> The fast brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 69

Slide 70

Slide 70 text

© 2016 Reginald Braithwaite. Some rights reserved. 70

Slide 71

Slide 71 text

class Buffer { append (theirEdit) { this.history.forEach( (myEdit) => { theirEdit = theirEdit.prependedWith(myEdit); }); return new Edit(this, theirEdit).doIt(); } } © 2016 Reginald Braithwaite. Some rights reserved. 71

Slide 72

Slide 72 text

class Buffer { appendAll(otherBuffer) { otherBuffer.history.forEach( (theirEdit) => this.append(theirEdit) ); return this; } } © 2016 Reginald Braithwaite. Some rights reserved. 72

Slide 73

Slide 73 text

alice.appendAll(bob); //=> My fast brown fox jumped over the lazy dog bob.appendAll(alice); //=> My fast brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 73

Slide 74

Slide 74 text

! © 2016 Reginald Braithwaite. Some rights reserved. 74

Slide 75

Slide 75 text

© 2016 Reginald Braithwaite. Some rights reserved. 75

Slide 76

Slide 76 text

let GUID = () => ??? class Buffer { constructor (text = '', history = []) { let befores = new Set(history.map(e => e.guid)); history = history.slice(0); Object.assign(this, {text, history, befores}); } share () { return new Buffer(this.text, this.history); } } © 2016 Reginald Braithwaite. Some rights reserved. 76

Slide 77

Slide 77 text

class Edit { constructor (buffer, { guid = GUID(), befores = new Set(), replacement, from, to }) { this.buffer = buffer; befores = new Set(befores); Object.assign(this, {guid, replacement, from, to, befores}); } } © 2016 Reginald Braithwaite. Some rights reserved. 77

Slide 78

Slide 78 text

class Buffer { has (edit) { return this.befores.has(edit.guid); } perform (edit) { if (!this.has(edit)) { this.history.push(edit); this.befores.add(edit.guid); return edit.doIt(); } } } © 2016 Reginald Braithwaite. Some rights reserved. 78

Slide 79

Slide 79 text

class Buffer { replaceWith (replacement, from = 0, to = this.length()) { let edit = new Edit(this, {replacement, from, to, befores: this.befores} ); return this.perform(edit); } } © 2016 Reginald Braithwaite. Some rights reserved. 79

Slide 80

Slide 80 text

class Buffer { append (theirEdit) { this.history.forEach( (myEdit) => { theirEdit = theirEdit.prependedWith(myEdit); }); return this.perform(new Edit(this, theirEdit)); } } © 2016 Reginald Braithwaite. Some rights reserved. 80

Slide 81

Slide 81 text

class Buffer { appendAll(otherBuffer) { otherBuffer.history.forEach( (theirEdit) => this.has(theirEdit) || this.append(theirEdit) ); return this; } } © 2016 Reginald Braithwaite. Some rights reserved. 81

Slide 82

Slide 82 text

class Edit { prependedWith (other) { if (this.isBefore(other) || this.befores.has(other.guid) || this.guid === other.guid) return this; let change = other.netChange(), {guid, replacement, from, to, befores} = this; from = from + change; to = to + change; befores = new Set(befores); befores.add(other.guid); return new Edit(this.buffer, {guid, replacement, from, to, befores}); } } © 2016 Reginald Braithwaite. Some rights reserved. 82

Slide 83

Slide 83 text

© 2016 Reginald Braithwaite. Some rights reserved. 83

Slide 84

Slide 84 text

let alice = new Buffer( "The quick brown fox jumped over the lazy dog" ); let bob = alice.share(); //=> The quick brown fox jumped over the lazy dog alice.replaceWith("My", 0, 3); //=> My quick brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 84

Slide 85

Slide 85 text

let carol = alice.share(); //=> My quick brown fox jumped over the lazy dog bob.replaceWith("fast", 4, 9); //=> The fast brown fox jumped over the lazy dog alice.appendAll(bob); //=> My fast brown fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 85

Slide 86

Slide 86 text

bob.appendAll(alice); //=> My fast brown fox jumped over the lazy dog alice.replaceWith("spotted", 8, 13); //=> My fast spotted fox jumped over the lazy dog bob.appendAll(alice); //=> My fast spotted fox jumped over the lazy dog carol.appendAll(bob); //=> My fast spotted fox jumped over the lazy dog © 2016 Reginald Braithwaite. Some rights reserved. 86

Slide 87

Slide 87 text

"Unfortunately, implemen2ng OT sucks. There's a million algorithms with different tradeoffs, mostly trapped in academic papers. The algorithms are really hard and 2me consuming to implement correctly." © 2016 Reginald Braithwaite. Some rights reserved. 87

Slide 88

Slide 88 text

perhaps we should borrow a trick from react, and periodically scan a "shadow buffer" for diffs that we exchange with collaborators… © 2016 Reginald Braithwaite. Some rights reserved. 88

Slide 89

Slide 89 text

differen'al synchroniza'on © 2016 Reginald Braithwaite. Some rights reserved. 89

Slide 90

Slide 90 text

this is a very big problem space © 2016 Reginald Braithwaite. Some rights reserved. 90

Slide 91

Slide 91 text

© 2016 Reginald Braithwaite. Some rights reserved. 91

Slide 92

Slide 92 text

class Buffer { replaceWith () { ... } share () { ... } append () { ... } appendAll () { ... } } © 2016 Reginald Braithwaite. Some rights reserved. 92

Slide 93

Slide 93 text

class Branch { commit () { ... } fork () { ... } cherryPick () { ... } merge () { ... } } © 2016 Reginald Braithwaite. Some rights reserved. 93

Slide 94

Slide 94 text

distributed version control © 2016 Reginald Braithwaite. Some rights reserved. 94

Slide 95

Slide 95 text

with invoca+ons as first-class en++es, we can build algorithms and protocols mastering +me and change, from single apps to distributed systems © 2016 Reginald Braithwaite. Some rights reserved. 95

Slide 96

Slide 96 text

© 2016 Reginald Braithwaite. Some rights reserved. 96

Slide 97

Slide 97 text

"Never confuse the example given of a pa6ern, with the underlying idea the pa6ern represents." © 2016 Reginald Braithwaite. Some rights reserved. 97

Slide 98

Slide 98 text

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