Immutable data

Examples using javaScript

by Ben Bakhar

benb@codeoasis.com

15 minutes talk

Immutable object

In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created (wikipedia)

Some arguments in favor of immutability

 

  • simpler application development
  • Thread safe
  • No defensive copying ( " You must program defensively with the assumption that clients of your class will do their best to destroy its invariants." )
  • Simple change detection techniques

Immutable persistent data collections for Javascript which increase efficiency and simplicity.

let list = Immutable.List([1, 2, 3]);
let changed = list.push(4);
list.toString();    // List [ 1, 2, 3 ]
changed.toString(); // List [ 1, 2, 3, 4 ]
list === changed    // false

Each change on the collection results with a new object created

compared to mutable Javascript list

let list = [1, 2, 3];
list.push(4);
console.log(list); // [1, 2, 3, 4]

AngularJS data-binding

$scope.collection = generateHugeCollection();
$scope.$watchCollection('collection', function (val) {
  // do some stuff with the changed value
});
  • register a watcher to watch 'collection'
  • evaluate at least once on each digest loop
  • compare current value (reference) to previous one, cheap.
  • equality check, expensive.

Drawback:  Although we gain a faster $watch there’s a big overhead of creating a new data structure once we add or remove items 

Summary

  • Fast change detection
  • Slow insertions and deletions

Can we take the best of both worlds?

Goals:

  • reduce $watch collection complexity from O(n) to O(1)
  • overcome the overhead of creation the new data structure on change

Ideas?

// ...
for (key in newValue) {
  if (newValue.hasOwnProperty(key)) {
    newLength++;
    newItem = newValue[key];
    oldItem = oldValue[key];

    if (key in oldValue) {
      bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
      if (!bothNaN && (oldItem !== newItem)) {
        changeDetected++;
        oldValue[key] = newItem;
      }
    } else {
      oldLength++;
      oldValue[key] = newItem;
      changeDetected++;
    }
  }
}
// ...

AngularJS $watchCollection implementation:

Use $watchCollection(expr, fn) but reduce its running time. This means that we need to decrease the elements over which $watchCollection iterates.

  • reduce the keys over which AngularJS’ dirty checker iterates => keep a control flag which handles collection state instead of creating a new reference

Suggestion

Implementation

function defineProperty(obj, name, descriptor) {
  'use strict';
  Object.defineProperty(obj, name, descriptor);
}

function defineMethod(obj, name, method) {
  'use strict';
  defineProperty(obj, name, {
    enumerable: false,
    value: method
  });
}

function VersionableMap(data) {
  this._version = 0;
  defineProperty(this, '_data', {
    value: data || {},
    enumerable: false
  });
}
defineMethod(VersionableMap.prototype, 'set', 
    function (key, value) {
      this._data[key] = value;
      this._updateVersion();
    });

defineMethod(VersionableMap.prototype, 'get', 
    function (key) {
      return this._data[key];
    });

defineMethod(VersionableMap.prototype, 'remove', 
    function (key) {
      var value = this._data[key];
      if (this._data[key] !== undefined) {
        delete this._data[key];
        this._updateVersion();
      }
      return value;
    });

defineMethod(VersionableMap.prototype, 'keys', 
    function () {
      return Object.keys(this._data);
    });

Results

Immutable data

By Ben Bakhar

Immutable data

  • 1,357