Immutability

How NOT to worry about side effects

State

vs.

Pure Functions

State

  • Result depends on it

function Adder(first, second) {
  this.first = first;
  this.second = second;

  this.sum = function () {
    return this.first + this.second;
  }
}

var adder = new Adder(2, 3);
adder.sum();

State

  • Introduces an additional parameter: TIME

function Adder(first, second) {
  ...

  this.setFirst = function(first) {
    this.first = first;
  }

  this.setSecond = function(second) {
    this.second = second;
  }
}

var adder = new Adder(2, 3);
... //black hole :P
adder.sum(); // =?

Those who don't play nice

function badGuy(param) {
  param.first = 10;
  param.second = -1;
}

var adder = new Adder(2, 3);
badGuy(adder) //i don't know what bad guy does
adder.sum(); // =5, right? :P
  • don't mutate the parameters inside a function (seriously, don't!)

Pure functions

function Adder() {
  this.sum = function(first, second) {
    return first + second;
  }
}

var adder = new Adder();
adder.sum(2, 3); // =5, always!
  • no state

  • depend only on parameters

  • play very nice with immutable objects

Case Study

-------------               ---------------
|           |     items     |             | 
|   Loader  |    ------->   |  Presenter  | 
|           |               |             |
-------------               ---------------
  • can select items in the presenter

function markSelected(item) {
  item.selected = true
}
-------------               ---------------
|           |     items     |             | 
|   Loader  |    ------->   |  Presenter  | 
|           |               |             |
-------------               ---------------

               \            ---------------
                \  items    |             |
                 ------->   |2nd Presenter|
                            |             |
                            ---------------
  • can select items in the second presenter

item.selected = true ???
-------------               ---------------
|           |     items     |             | 
|   Loader  |    ------->   |  Presenter  | 
|           |               |             |
-------------               ---------------

               \            ---------------
                \  items    |             |
                 ------->   |2nd Presenter|
                            |             |
                            ---------------
  • reload data from the server

fetchData(...)
    .then(loadedData => this.myData = loadedData)

that easy?

  • the presenters have just lost the selected state

  • start doing all sort of complex logic

fetchData(...)
    .then(loadedData => {
        this.myData.length = 0;
        for (let item of loadedData) {
            this.myData.push(item);
        }
    })
  • the presenters are f**ked again...

fetchData(...)
    .then(loadedData => {
        this.oldData = copy(this.myData);
        this.myData.length = 0;
        for (let item of loadedData) {
            if (isSelectedByFirstPresenter(item, this.oldData)) {
                item.selected = true;
            }
            this.myData.push(item);
        }
    })


function isSelectedByFirstPresenter(item, oldData) {
    //indexOf won't work because it's a new object
    return this.oldData.any((oldItem) => equals(oldItem, item) && oldItem.selected);
}

what is selected and why do i know about it?

In short

  • exposed to unwanted changes
  • no real owner of the data

  • have to take care/account for others

  • complex equality checks for complex objects

There is no silver bullet

There is no silver bullet

X

Immutability

Immutable objects

  • can't change an object
  • can only create a new, changed object
  • data ownership
let state = {
  who: 'Ana',
  has: 'apples',
  count: 5
}

state.count = 6;

expect(state.count).to.equal(6);
let state = {
  who: 'Ana',
  has: 'apples',
  count: 5
}

let nextState = state.set('count', 6)

expect(nextState.count).to.equal(6);
expect(state.count).to.equal(5);

No things in life are free

  • loader and presenter can't share the same list if they want to change it
  • each handles it's own s***t:
    • loader loads data and gives it to the presenters
    • they dirty it up with whatever they want

No things in life are free

  • memory consumption
  • garbage collection usage

really? :P

  • data event subscription
  • complex bookkeeping
  • sync issues
  • copy = deep cloning
  • unidirectional data flow
  • react to data
  • easily detect data changes (equality check)
  • copy = add another reference => O(1)
  • anything is undo-able
  • lazy operations - 100% the data will not change

Before & After

Memory

let brown = Range(0, 9);
let blue = brown.set(5, 'beef');

Examples

Lazy Sequence

Tips

  • use immutable objects (duh :P)
    • API, module interfaces, communicating with the view
  • don't mix mutable and immutable data in the same place
  • familiarise with FP (it's here to stay)

Up next:

Q & A

Thanks!

Resources

  • https://facebook.github.io/immutable-js/
  • http://hypirion.com/musings/understanding-persistent-vector-pt-1
  • https://idea.popcount.org/2012-07-25-introduction-to-hamt/
  • http://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript

state

By Horia Radu

state

  • 292