The Mutable State Monster and How to defeat it
Anup Cowkur
Prologue
What is state?
Wikipedia:
A computer program stores data in variables, which represent storage locations in the computer's memory. The contents of these memory locations, at any given point in the program's execution, is called the program's state
State is the current value of data at any point of execution of the program
Chap 1:
The Wizard
A long time ago, there was a grand wizard
John McCarthy
He pioneered Artificial Intelligence
In 1958, He came up with a little language called LISP
LISP
LISP was the first language to have automatic memory management using a Garbage Collector
It was also one of the first language to have immutability built in via Atoms. Data that could not be divided further
But why?
Chapter 2:
The Monster
Does this function work correctly if 2 threads call it at the same time?
public void checkAndPut(final String key,
final String value) {
if (!map.containsKey(key)) {
map.put(key, value);
}
)
Check-Then-Act
Fix
public void checkAndPut(final String key,
final String value) {
synchronized(this) {
if (!map.containsKey(key)) {
map.put(key, value);
}
}
)
How about this one?
public class EntCounter {
private long count = 0;
public long increment() {
return ++count;
}
}
Read-Modify-Write
Fix
public class EntCounter {
private long count = 0;
public long increment() {
synchronized(this) {
return ++count;
}
}
}
And this one?
public class RunFrodoRun {
private boolean stopped = false;
public void run() {
while(!stopped) {
// RUN!!!
}
}
public void stop() {
this.stopped = true;
}
}
The value of stopped might be read from a thread-local variable in the JVM or the L1/L2 cache instead of main memory
Visibility
Fix
public class RunFrodoRun {
private volatile boolean stopped = false;
public void run() {
while(!stopped) {
// RUN!!!
}
}
public void stop() {
this.stopped = true;
}
}
Last one, promise.
public class OrcFunerals {
int orcsKilledYesterday = 0;
int orcsKilledToday = 0;
public void init() {
orcsKilledYesterday = 1;
orcsKilledToday = 2;
}
public int getDeadOrcs() {
return orcsKilledYesterday + orcsKilledToday;
}
}
The JVM provides no guarantees about order of execution of incorrectly synchronized operations
Ordering
Fix
public class OrcFunerals {
int orcsKilledYesterday = 0;
int orcsKilledToday = 0;
public synchronized void init() {
orcsKilledYesterday = 1;
orcsKilledToday = 2;
}
public synchronized int getDeadOrcs() {
return orcsKilledYesterday + orcsKilledToday;
}
}
So we fixed it, right?
Local fixes are simple, but the complexity increases exponentially as the system gets larger
Real world systems
source: http://www.whattofix.com/images/ComplexERDExample.gif
Big systems compound complexity exponentially and lead to deadlocks, livelocks and other bugs that are hard to reason about
Our tiny programmer brains are simply not smart enough to deal with complexity at this scale
When sufficient complexity coalesces with state mutation, the mutable state monster is born
Mutable State Monster
Chapter 3:
The Prophet
Gordon Moore
Moore's Law:
The number of transistors in a dense integrated circuit doubles approximately every two years
This hasn't really been true in the last decade
We cannot stuff more transistors into our processors anymore
We must make our programs able to easily run on multi-threaded systems
We're gonna have to...
Chapter 4:
The fellowship of the functional programmers
John McCarthy, Alonzo Church, Haskell Curry, Rich Hickey...
They were thinking of an alternative way of representing computation
Can we represent programs as a series of data transformations instead of a series of sequential data mutations?
Imperative
int[] numbers = {5,10,15};
int sum() {
int total = 0;
for(int i=0; i<numbers.size ; i++) {
total = total + numbers[i];
}
return total;
}
Functional
sum(sum(sum(0,5), 10), 15)
int sum(x,y) {
return x + y;
}
We don't need to know everything about functional programming. We just need to steal some good ideas
Referential Transparency
Referential transparency means there is no difference between the value and reference of a piece of data
Immutability
Immutability means data once created cannot be modified
Mutable Library
public class Library {
public List<String> books;
public void addBooks(List<String> newBooks){
books.addAll(newBooks);
}
}
Immutable Library
public class Library {
// 1. Make list of books private
private final List<String> books;
public Library(List<String> books) {
this.books = books;
}
public void addBooks(List<String> additionalBooks){
// 2. Copy old books
List<String> oldBooks = copy(books);
// 3. Add additional books to copy of old books
List<String> newBooks = oldBooks.addAll(additionalBooks);
// 4. Create and return new Library with new
// list of books
return new Library(newBooks);
}
}
But this will create too many objects, no?
Structural Sharing
Auto-complete trie
Persistent data structures can be more space efficient
The Oxford english dictionary has 228,132 words which would take the same number of arrays to store if we didn't use a trie
Clojure and Scala (both JVM languages) have very efficient implementations of persistent data structures
Modelling our Library updates in a functional way
Chapter 5:
A New Age
Functional programming is increasing in popularity
Frontend
Backend
What about good old Android?
Java 8 on Android introduced lambdas and method references. No Streams yet 😞
We have Rx to transform data
There are interesting projects for persistent immutable data structures on Android. Not widely battle tested.
https://github.com/konmik/solid
https://github.com/andrewoma/dexx
Kotlin has Quasar library for Erlang style actors and go style CSP concurrency
What we don't have, we can build together
You don't have to do it all at once
And you don't have to do it alone
Epilogue
The mutable state monster can never be completely eliminated
But if we are open minded enough to learn from other programming paradigms, it can be contained and defeated
The future is bright
Thank you!
@anupcowkur
The Mutable State Monster and How to defeat it.
By Anup Cowkur
The Mutable State Monster and How to defeat it.
- 953