Talk Structure

  • Why Immutability in JS (2 min)
  • The Immutable.js API (2 min)
  • Problems with the API (1 min)
  • Introducing ES6 Proxies (1 min)
  • Live coding local mutability (2 min)
  • Inserting immer (1 min)
  • Demoing more immer functionality (1 min)
  • Immutability !== PIDS (2 min)
  • General lessons we can draw (3 min)
    • Take something that works well, make it less annoying
    • Small APIs > your own DSL
    • Scope is incredibly powerful
    • DX Mullet

Details

  • Why Immutability in JS (2 min)
    • Traditionally: fearless concurrency
    • No moving parts
      • HDD have MBTF of 500k, SSDs have MBTF of 2.5m
      • More debuggable, less breakable
    • Answering "what changed"
export function foo() {
  var data = { key: 'value' };
  touchFn(data);
  console.log(data.key); // ???
}
- you can either:
  - use Object.observe, dirty bits, "change detection"
  - or you can make it immutable and use reference equality
- reconciliation goes from O(N^3) to O(N) to O(log N)
  • The Immutable.js API (2 min)
export function foo() {
  var data = Immutable.Map({ key: 'value' });
  touchFn(data);
  console.log(data.get('key')); // value
}
  • Problems with the API (1 min)
    • Learning curve
    • Spreading/Interop
      • no destructuring
      • but doesnt work with anything else so you .toJS()
    • Difficult to Debug
      • browser formatter
    • In summary: PIDS
    • DX Mullet
  • Introducing ES6 Proxies (1 min)
var target = { foo: 'bar' };
var handler = {
  get(obj, prop) {
    return 42;
  }
};
var wrappedTarget = new Proxy(target, handler);
console.log(target.foo); // 'bar'
console.log(wrappedTarget.foo); // 42
var target = { foo: 'bar' };
var handler = {
  get(obj, prop) {
    console.log(`accessing ${prop}!`);
    return obj[prop];
  }
};
var wrappedTarget = new Proxy(target, handler);

console.log(target.foo); // 'bar'
// accessing bar!
console.log(wrappedTarget.foo); // 'bar'
var target = { foo: 'bar' };
var handler = {
  get(obj, prop) {
    console.log(`accessing ${prop}!`);
    return Reflect.get(obj, prop); // do what you're supposed to do
  }
};
var wrappedTarget = new Proxy(target, handler);

console.log(target.foo); // 'bar'
// accessing bar!
console.log(wrappedTarget.foo); // 'bar'
var target = { foo: 'bar' };
var handler = {
  get(obj, prop) {
    return Reflect.get(obj, prop);
  },
  set(obj, prop, value) {
    console.log(`setting ${prop}!`);
    return Reflect.set(obj, prop, value);
  }
};
var wrappedTarget = new Proxy(target, handler);

wrappedTarget.foo = 'baz';
// setting foo!
function produce(base, recipe) {
  let newcopy = { ...base };
  const handler = {
    get(target, name) {
      return Reflect.get(target, name);
    },
    set(target, name, value) {
      newcopy[name] = value;
      return true; // setting was "successful"
    }
  };
  const draft = new Proxy(base, handler);
  recipe(draft);
  return Object.freeze(newcopy);
}
const obj1 = { foo: 'bar' };
const obj2 = produce(obj1, draft => {
  // local mutability!
  draft.foo = 'forwardJS';
});

console.log('obj1', obj1.foo); // 'bar'
console.log('obj2', obj2.foo); // 'forwardJS'
  • Live coding local mutability (2 min)
  • Inserting immer (1 min)
import produce from 'immer';

const obj1 = [{ foo: 'bar', qux: 4 }];
const obj2 = produce(obj1, draft => {
  draft[0].foo = 'forwardJS';
  draft.push({ foo: 'baz' }); // API = JS
  const { foo, qux } = draft[0]; // destructuring
});
  • Demoing more immer functionality (1 min)
  • Immutability !== PIDS (2 min)
    • we wanted Immutability but got Immutable.js
    • Immutable.js combines PIDS with Immutability with utility functions
    • Immer achieves Immutability with plain JS
  • General lessons we can draw (3 min)
    • Take something that works well, make it less annoying
    • Small APIs > your own DSL
    • Scope is incredibly powerful
    • always bet on JS

memes

misc

  • Dec 2013 - David Nolen Om - wrapper around React that was faster than React