🔥 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
- Start at root
- Walk the path
- Collect items
4,2,1,2
4,1,3
4,2
bar
00 01 10 11
Bitmapped Vector Trie
- Start at root
- Walk the path
- 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) = ?
- 32-bit number
- 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
List, Stack, Map, OrderedMap, Set,
OrderedSet and Record
Bridge into ClosureScript's data structures
Reactive & immutable
Immutable data structures with native JS
APIs
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 ❓
This Immutable Thing
By Tobias Deekens
This Immutable Thing
Explains a few things about immutable data structures.
- 1,481