🔥 Escaping mutability hell 🔥

...with persistent immutable data structures

Ideas, concepts & advantages

Why

How

Who

Le Fin

Why

Call stack based reverse architecture engineering (CSBRAE)

Why

...random Twitter quote

Removing lines of code is good, but removing variable state is even better!

Why

Assumptions & Premises

const fn = (T) => {
  helper(T)
  
  console.log(T)
}
class Component extends ... {
  constructor(settings, model) {
    super(this)
    
    this.model = model
    this.settings = settings
  }

  render() {
    if (!settings.valid) { return }
  }
}

class Model extends ... {
  constructor(settings, user) { }

  get item() {
    if (settings.hasAccess(user)) {
      return this.store('something')
    }
  }
}

Shared state

...in OOP the good ok parts

💔 Isolated System I

💜 Isolated System II

S
B
B
B
B
S
S
S
B
S

Shared state

...in OOP the messed up parts

💔 Isolated System I

💜 Isolated System II

S
B
B
B
B
S
S
S
B
S
B
S

Why

...do some inventory

 

  • Convoluted shared state

    • ...in objects over time

 

  •  The ”what changed problem”

    • Object observing is odd (change records)

    • Dirty flags are nasty (coupling)

  • Avoiding the "Many Mutators"-Problem

    • ​State races (concurrently)

State over time

...the root of all evil!

Time

State is value(s) over time (complected)

Initialisation

Mutation

Reassignment

Unsetting

...the programm needs to keep track

...the programm needs to remain consistent

State over time

...cascades and tickles

A 1

A 2

A 3

A 4

A 5

B 1

B 2

B 3

B 4

B 5

How

...rip state out (to edges)

State Axiom

Side Effects

State Transformations

...a note on JavaScript I

Semi immutable

Semi mutable

Array.filter, Array.slice...

Array.sort, Array.push...

Conceptual trap

...people think in state and mutations

...a note on JavaScript II

Semi immutable

Semi mutable

const THRESHOLD = 13

// 💣 THRESHOLD = 9
const CONFIG = {
   threshold: 13,
   api: 'v2'
}

// ⚠️ CONFIG.threshold = 2
const items = Object.assign(
  {}, 
  { title: 'Buy milk', done: false }
  { title: 'Chocolate', done: true }
)
const items = { ...item1, ...item2 }
let a = { x: 1 }
let b = Object.assign({}, a)

console.log(b.x === a.x)
//true
const b = { ...a }

Persistent

a copy - not destroyed

Immutable

no change

Data Structure

there's more to it (DAG)

How

a note on APIs and languages

🍦 Frozen data

⛽️ DAG as library

⭐️ DAG in language

#fancyness

Closure

Scala

JavaScript

Java

#any

Haskell

Persistent & Immutable

let cars = ['VW', 'Scoda'];

let something = cars.push('Fiat');

something === 3;
cars.length === 3;
The mutation game
Array.prototype.immutable = (item) => {
  let copy = this.slice(0);
  copy.push(item);
  return copy;
};

let drinks = ['Beer', 'Mate'];

let moreDrinks = drinks.immutable('Water');

(moreDrinks === drinks) === false;
New shiny things

...never edit the original.

...yes it is slower - but by a fixed magnitude!

Referential intransparent
Referential transparency

...only edit what's needed!

Speed & Performance

...share the commonality

A

C

X

D

let list2 = list1.immutableSet(1, "E")

A

C

X

D

let list1 = 

let list1 = 

let list2 = 

A

E

}

Data structure

Directed

Acyclic

Graph

Data in the DAG I

...data in the Trie (re Trie val)

4

2

1

1

2

3

3

2

Standard Trie

  1. Start at root
  2. Walk the path
  3. Collect items

4,2,1,2

4,1,3

4,2

bar

00  01  10  11

Bitmapped Vector Trie

  1. Start at root
  2. Walk the path
  3. Data at leaf

00  01  10  11

ba

bad

00  01  10  11

bb

00  01  10  11

1110 = bb

001010 = ba

Data in the DAG II

...hash-Trie

00     01    10    11

00     01    10    11

00     01    10    11

00     01    10    11

"Bathrobe"

"Tobi"

42

dag.get("Tobi")
dag.hash("Tobi") === 001011

dag.set("Bathrobe") // 000100

dag.hash([1,2,3]) === 32-Bit Number!

hash(thing) = ?

  1. 32-bit number
  2. as binary

Stuctural Sharing

  • Rewire connection
  • Add intermediary nodes
  • Only change path nodes

Structural Sharing

...share the DAG

Initial DAG

Copied node

Inserted

Shared

Garbage

(maybe) collected

Data in the DAG III

...path nodes change

00     01    10    11

00     01    10    11

00     01    10    11

00     01    10    11

"Company"

"Tobi"

42

00     01    10    11

00     01    10    11

00     01    10    11

"💣"

💣

💣

hash("GmbH") =
hash("💣") =
11 10 11 11
11 10 11 11
01 11 10
11 01 11

"GmbH"

00     01    10    11

Speed of the DAG

...it's almost constant

O(1) * O(log n) O(n)
append concat
first insert
last
nth
update

* O( log32 n): n being length of vector being at

   most MAX_INTEGER makes it ~7

  • Node width of 32 maps to hardware
     
  • 32^7 = 34 359 738 368

Recycling & Sharing

...solving the what changed problem

Mutable

Immutable

let model = {
  name: 'Tobias',
  age: 19,
  height: 203
};

view(model).render();

model.age = 20;

// 1. Manually
view.render();

// 2. Eventing
model.change(view.render);

// 3. Setters as reach-in
view.setModel('age', 19);
let model = Immutable.Map({
  name: 'Tobias',
  age: 19,
  height: 203
});

view(model).render();

modelWithNewAge = model.set('age', 19);

// Reference equality
(modelWithNewAge === model) === false;
model.get('name') === modelWithNewAge.get('name');
model.get('age') !== modelWithNewAge.get('age');

// Re-render with early exit in equality check
view(modelWithNewAge).render();

Nested Structures

...data structures in data structures

let presenter = Immutable.Map({
  personal: Immutable.Map({
    name: 'Tobias',
    age: 19,
    height: 203,
  },
  address: Immutable.Map({
    city: 'Rastede',
    zip: 20180
  });
});


let relocatedPresenter = presenter
  .setIn(
     ['address', 'city'], 'Berlin'
  ).setIn(
     ['address', 'zip'], 10437
  );

relocatedPresenter.get('address') === presenter.get('address') // false
relocatedPresenter.get('personal') === presenter.get('personal') // true

Referencial Transparency

...old skool goes new skewl!

 

  • No {shallow, deep} Equals

    • Complex & expensive

  • Memoization

    • ​​No serialization with e.g. O(n)

  • Abstract at right place

    • ​Data Structure level not with model

↪ 

know when to do work!

Who

...some refs to read up

Immutable.js

   List, Stack, Map, OrderedMap, Set,  

   OrderedSet and Record

Mori

   Bridge into ClosureScript's data structures

Freezer

  Reactive & immutable

seamless-immutable

​  Immutable data structures with native JS  

  APIs

Baobab & Updeep

  Cursors/pointers to watch immutable

  data structures

💕 React, PReact, Inferno et al. 💕

with DAG

without DAG

Ups & Downs

...new bullets are always silver

Rigorous

    One-off & Communicates

Performant

   Structural Sharing

    No accidental re-renderings

Integrates with CQRS

   Event log (redux or even API)

    Replay state at any time

Another API & Paradigm

    Learning curve

    Lock-in & integration friction

Development & Debugging

   Non-native to JS

    .toJS | log OR response | fromJS

Communication

   Align all those developers

Conclusion

stay safe - stay immutable

Mutation

Immutability

...is a mistake or accident

reduces accidental complexity

❗️ Thanks ❗️

❓ Questions

Made with Slides.com