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.

  • 865