Slide 1

Slide 1 text

“Immutable”

Slide 2

Slide 2 text

What is Immutability?

Slide 3

Slide 3 text

var statement = "I am an immutable value”; var otherStr = statement.slice(8, 17);

Slide 4

Slide 4 text

In JavaScript, Mutability Abounds

Slide 5

Slide 5 text

var arr = []; var v2 = arr.push(2);

Slide 6

Slide 6 text

Elixir & Clojure

Slide 7

Slide 7 text

Immutability iex(1)> list = [:a, :b, :c] [:a, :b, :c] iex(2)> List.delete(list, :b) [:a, :c] iex(3)> list [:a, :b, :c] iex(4)> list = List.delete(list, :b) [:a, :c] iex(5)> list [:a, :c]

Slide 8

Slide 8 text

defmodule MyList do def length(the_list) do length(the_list, 0) end def length([], count) do count end def length([_head|tail], count) do length(tail, count + 1) end end

Slide 9

Slide 9 text

Why immutable collections?

Slide 10

Slide 10 text

1. Improved caching

Slide 11

Slide 11 text

Let’s say you have a user profile, represented as a JavaScript object: var profile = { name: 'Scott', user_id: 42, pic: 'https://scott.mn/me.jpg', friends: [... array of objects ...], latest_tweets: [... array of objects ...], ... }; When one piece of data, let’s say the profile pic, changes: profile.pic = 'https://scott.mn/me2.jpg';

Slide 12

Slide 12 text

function arrayEqual(a, b) { if (a.length !== b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } var FriendsView = { render: function(profile) { if (arrayEqual(profile.friends, this_lastFriendsValue)) { return; } // use .slice() to make a copy this._lastFriendsValue = profile.friends.slice(); // ... actually update the DOM ... } }

Slide 13

Slide 13 text

In Clojure, immutable data structures are the default and you have to really go out of your way to create a mutable array, so this problem doesn’t come up much. But in JavaScript …..

Slide 14

Slide 14 text

Undo and redo ❖ Store the entire state of your app in a tree of immutable collections. ❖ Keep a stack of all the states you’ve ever seen, pushing the new state into the stack when something changes. ❖ Pop the stack when the user wants to undo.

Slide 15

Slide 15 text

No defensive copying An example of where this can bite you is the options hashes many JavaScript APIs take: var options = {foo: true}; lib.doAThing(options); lib.doAnotherThing(options); Innocuous enough, but what if the library is doing something like this: exports.doAThing = function(options) { // prepare in some way whatever(); // then delegate to another method options.silent = true; exports.doAnotherThing(options); }; exports.doAnotherThing = function(options) { // ... behaves completely differently if options.silent is true ... };

Slide 16

Slide 16 text

More reasons?

Slide 17

Slide 17 text

React

Slide 18

Slide 18 text

http://jsbin.com/sazupukide/edit?js,output handleChangeSchoolSameValue() { this.setState((data) => ({ user: { school: { high: data.user.school.high, middle: 'middleSchoolName' } } })); }, shouldComponentUpdate(nextProps, nextState){ return ( !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState) ); }

Slide 19

Slide 19 text

If you mutate data like this: myData.x.y.z = 7; // or... myData.a.b.push(9); var newData = deepCopy(myData); newData.x.y.z = 7; newData.a.b.push(9);

Slide 20

Slide 20 text

Unfortunately, deep copies are expensive, and sometimes impossible. var newData = extend(myData, { x: extend(myData.x, { y: extend(myData.x.y, {z: 7}), }), a: extend(myData.a, {b: myData.a.b.concat(9)}) }); var newData = React.addons.update(myData, { x: {y: {z: {$set: 7}}}, a: {b: {$push: [9]}} });

Slide 21

Slide 21 text

http://swannodette.github.io/todomvc/

Slide 22

Slide 22 text

https://github.com/omcljs/om

Slide 23

Slide 23 text

Immutable-js

Slide 24

Slide 24 text

var Immutable = require('immutable'); var map1 = Immutable.Map({a:1, b:2, c:3}); var map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); // 50

Slide 25

Slide 25 text

https://github.com/facebook/immutable-js

Slide 26

Slide 26 text

http://jsbin.com/mujihegowi/edit?js,output handleChangeSchoolSameValue() { this.setImmState(d => d.updateIn(['user','school', 'middle'], (v)=> 'middleSchoolName2')) }, shouldComponentUpdate(nextProps, nextState){ return ( !shallowEqualImmutable(this.props, nextProps) || !shallowEqualImmutable(this.state, nextState) ); },

Slide 27

Slide 27 text

structural sharing

Slide 28

Slide 28 text

https://github.com/facebook/ immutable-js

Slide 29

Slide 29 text

https://github.com/cjohansen/ react-sweeper

Slide 30

Slide 30 text

Problems http://noredinktech.tumblr.com/post/107617838018/ switching-from-immutablejs-to-seamless-immutable

Slide 31

Slide 31 text

Questing for a Guarantee

Slide 32

Slide 32 text

❖ Fully immutable collections: once instantiated, it’s guaranteed that nothing about them can change. ❖ As little effort as possible needed to use them with JS libraries. ❖ No surprises; APIs follow established conventions.

Slide 33

Slide 33 text

var obj = {foo: "original"}; var notFullyImmutable = Immutable.List.of(obj); notFullyImmutable.get(0) // { foo: 'original' } obj.foo = "mutated!"; notFullyImmutable.get(0) // { foo: 'mutated!' } Problem 1: Like Object.freeze, the collections are only shallowly immutable. In other words, any mutable objects we place in immutable.js collections remain mutable.

Slide 34

Slide 34 text

Problem 2: We had to do a lot of converting back and forth to go from immutable.js collections to vanilla JS collections. It surely wasn’t as bad as the quick-and-dirty “serialize to JSON” hack would have been, but it was a recurring pain nevertheless. var hobbits = Immutable.fromJS([{name: "Frodo"}, {name: "Samwise"}, {name: "Meriadoc"}]) _.pluck(hobbits, "name") // Runtime exception _.pluck(hobbits.toJS(), "name") // ["Frodo", "Samwise", "Meriadoc"]

Slide 35

Slide 35 text

https://github.com/facebook/immutable-js/issues/23 var v = Immutable.Vector(1, 2, 3) v.toString() // "Vector [ 1, 2, 3 ]" var v2 = v.map(function (x) { return x + 1; }) v2.toString() // "Seq [ 2, 3, 4 ]" v2.delete(0) // TypeError: undefined is not a function

Slide 36

Slide 36 text

https://github.com/rtfeldman/ seamless-immutable

Slide 37

Slide 37 text

Integrating with Third-Party Libraries An unanticipated benefit of this was realizing we could use much of Underscore’s library of functions as normal. When we wanted to use _.max or _.find, we passed them a seamless-immutable array and everything just worked.

Slide 38

Slide 38 text

There are a few ways to resolve this. One way is to use asMutable, which returns a vanilla JS array representation of a seamless-immutable array. Another is to use another library’s map function, such as _.map from Underscore. In CoffeeScript, you also can use a list comprehension (for…in) instead of map to similar effect. https://github.com/u2/react-alt-demo/blob/master/assets/javascripts/components/ CommentList.jsx https://github.com/facebook/immutable-js#embraces-es6

Slide 39

Slide 39 text

Performance Three performance-related features that we lost when switching from immutable.js to seamless-immutable are lazy sequences, batching mutations, and Persistent data structures under the hood.

Slide 40

Slide 40 text

The Value of Values http://www.infoq.com/presentations/Value- Values

Slide 41

Slide 41 text

http://gotocon.com/dl/goto- cph-2012/slides/value-of-values.pdf

Slide 42

Slide 42 text

http://facebook.github.io/react/docs/update.html http://swannodette.github.io/todomvc/ http://boke.io/immutable-js/ http://www.zhihu.com/question/28016223 https://scott.mn/2014/04/27/why_immutable_collections/ http://www.sitepoint.com/immutability-javascript/ http://www.sitepoint.com/immutability-react/ http://noredinktech.tumblr.com/post/107617838018/switching-from-immutablejs-to- seamless-immutable http://www.infoq.com/presentations/Value-Values http://gotocon.com/dl/goto-cph-2012/slides/value-of-values.pdf http://www.sitepoint.com/elixir-love-child-ruby-erlang/