Immutable Data Structures and JavaScript
jlongster.com
jlongster.com
I also find myself often having to do trial and error stuff to fix my code (also while in a paused state in the console). I mean it's pretty nice that you can actually do this, don't get me wrong. But overall, I am slower and less productive with immutablejs than I am with vanilla JS / JSON objects.
It's a trade off people really should keep in mind before pulling the trigger on immutable data structures. Sure, you get that performance boost, but do you actually need it? Are your React views really so slow (or are you running on slow embedded hardware like I am)? Or are you just drinking the kool aid because immutable data is hot right now?
You get runtime perf improvements in certain cases, at the cost of opaqueness, productivity and complexity. Make sure it is worth it.
- works with regular objects and arrays - runtime type checks - easy debugging with Chrome DevTools - immutability and immutability helpers - runtime type introspection - easy JSON serialization / deseralization - pattern matching
Also https://github.com/gcanti/redux-tcomb (Immutable and type-checked state and actions for Redux)
This looks like a nice alternative to seamless-immutable though.
Why is that? IMHO, this sounds like a problem with the dev tools more than anything.
If the whole state is one immutable tree, then the next state is a new tree and the whole app had to be rendered again.
I know it probably doesn't work like that, but this is what I think when I read about immutability.
If the toplevel object contains 3 items:
{ messages: list
users: list
me: object }
and the toplevel component contains 3 components that correspond to these 3 sub-states: MessageList, UserList, MyInfothen updating the user list will only cause a re-render of the toplevel component and UserList, but not MessageList or MyInfo or their child components (the same VDOM will be reused and no diff / patching will be performed on that part of the VDOM / DOM, respectively)
Something like
if (this.shouldComponentUpdate(...)) {
return this.render(...)
} else {
return this._oldVDOM;
}
then the diff algorithm can quickly compare by reference equality and only fall back to deeper object equality if the reference equality returns false.If libraries and tools rely on protocols instead of using built-in objects then we would be able to plug any kind of data structure implementing the required protocols into any kind of library that takes advantage of those protocols.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
From that perspective, immutable structures should be the default, with mutable structures being reserved for when they're truly needed.
In particular, if you need to apply any transformation to your model before displaying it in a view, it's not going to be a fast pointer check - you need to do dirty checks on all the elements just like you would with mutable data structures.
immutableEq(a1, a2) {
return (a1 === a2) || all(mapPair(a1, a2, immutableEq))
}
Now if you update the value of a single element in the array a2, you would not have to check all the elements in depth (only their references): a1 = Immutable.List(el1, el2, el3, el4, el5)
a2 = a1.set(2, Immutable.Map({hello: "world"}));
immutableEq(a1, a2) // all but el3 compared by reference equalityIf you have a pipeline then it's going to result in everything downstream re-running, unless you compare the output of the map() transformation to the previous value, like some build systems do.
This conforms with my understanding as to why Clojure is slowish. That being said, I don't really understand why Haskell is seen as fast and Clojure is seen as slow (I am ignoring JVM warm-up times here).
Do they use fundamentally different algorithms under the covers?
Clojure's immutable datastructures are ~25% slower than standard java, but also eliminate entire classes of bugs.
I'm not familiar with Haskell's immutable data structures, but I'd be very surprised if there's an optimization that hasn't been adopted by Clojure. Rich Hickey put a lot of work into optimizing them.
Angular did the same but went on a different path: watching the state (scope) for changes and then digesting the resulting spaghetti updates
React users are simply adopting well known techniques from functional programming languages because they work well with the virtual dom concept. Except for that concept, there is nothing new being invented here.
This mess was why people hated front-end programming. It has only been a year or two since Angular/React became mainstream and allowed us to think of state as plain JS objects. Lest we forget, it was truly bad times before.
You obviously haven't worked on any legacy app built in jQuery.
Until you try to build a large app and get murdered by maintainability problems (eg. treating the DOM as a store of mutable state, managing event listener binding lifecycles, etc) and performance issues (eg. DOM reads and writes to occurring in an interleaved fashion inside inner loops)
The vast majority of apps, when properly designed (and using things such as paginated datasets, and properly managing memory/cleaning up after themselves), do not need immutability.
You should first ask yourself, is this a problem that can be solved with immutable data structures, or why do I need to have huge data structures in the browser for my frontend? Unless it's a very special case, you can do more for frontend performance with lazy loading, and some good old fashioned performance analysis (take inventory of event listeners, look at a heap dump from Chrome dev tools, etc) than you can from adding the significant developer and cognitive overhead of immutable.js or its ilk.
It may seem nice to say if obj === obj2 and if any deeply nested things have changed it's just a pointer, but as others have mentioned this is not the primary use case for many UI's that are constantly recomposing and creating different kinds of data structures from other data structures (think map, creating a hash from an array, etc).
There is no silver bullet and premature optimization with a highly specific design pattern is the root of all evil. Don't worry about performance until there is a legitimate performance problem that needs to be worried about.
I think that's probably wrong. Well-implemented mutable collections are faster than functional collections, if for no other reason than that they allocate less. As others here have said, the argument for functional collections has almost always been on style and clarity grounds. In some cases there's a stronger argument because you need to have many slightly different versions of a collection existing in memory simultaneously; I have written a piece of code like that, but such situations are pretty rare.
I don't want to sound like I'm laughing at your argument, which is all true. It's just funny to see someone putting it that way.
In general, sure, if you can't take advantage of these semantics then there is going to be overhead and you just gain simplicity. But in certain domains we can take advantage of this simplicity for big performance wins.
Having them built in to the language would open up some interesting new possibilities too. It should be possible to send immutable data between Worker threads without the overhead of serialization and deserialization, which is currently one of the main barriers to doing heavy computation in a web worker rather than on the UI thread. Of course, there would be some nasty internal implementation details to sort out, as it would require sharing heaps, but it should be possible.
(Note that seamless-immutable also enforces immutability by using `Object.freeze` in development)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
The object spread operator (proposed in ES7) is essentially syntactic sugar for Object.assign:
https://github.com/sebmarkbage/ecmascript-rest-spread
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I've even used jQuery's extend in legacy projects:
A React.js lead even just wrote on twitter [2], "Starting to see a trend of premature fluxing of react apps. Try plain @reactjs first and bring in flux only when you absolutely need it."
Here's a screenshot of a pretty big project that scaled great with cursors: http://curator-lilita-10664.bitballoon.com/work-area-metadat.... We had all sorts of complexity scaling problems while building this, but none of them were due to state management, migrating to cursors made 99% of pain due to complicated ui state go away.
[1] https://github.com/dustingetz/react-cursor
[2] https://twitter.com/floydophone/status/649786438330945536
Now, functional programming is nice and lends itself some useful benefits, but unfortunately JavaScript just wasn't built in the value-oriented sense. And given the native features of JavaScript, such as the ability to create "classes," I'd like to take advantage of these features and reap all their benefits, such as encapsulation (which of course, some in the FP community don't seem to care about[1]).
I'd like to see a middle ground somewhere between having e.g. immutable objects yet respecting the way JavaScript is built. Until then, I'd have to ditch common JavaScript idioms, which are only being advanced in ES6. Not something I'm very keen on doing.
The only way to really eschew mutability is to ditch the concept of objects representing a state and a set of methods that mutate its state (which maintain encapsulation and enforce separation of concerns). Unless you decide to make all your prototype methods return new instances of the constructor upon mutation, which would be both inefficient and prone to error.
[1] https://github.com/aearly/icepick
*EDIT: Clarification
> For example, keys of a Map object are value equality checked... This has a lot of really nice implications.
I'm not sure how immutability helps here. And it's not possible to use reference equality, since with queries, you're converting user input into a data structure.
Edit: never mind. Precisely because you're dealing with input, you have to use value-equality.
queryCache.set(query, results);
vs queryCache = queryCache.set(query, results); const a = {}
// That's OK:
a.a = 1
// That's not OK:
a = 1
While I don't disagree with you on your point, one could easily create the counter-point that by using `const` you're implying something that's not true.If you truly do need to run a diff, you're going to run into the exact same optimization problems with both immutable values and mutable values.
So you would have to do: var stateB = JSON.parse(JSON.stringify(stateA))
to get a copy and then you can change stateB and it will not be equal to stateA.
The problem is without use of actual "persistent data structures" (not "immutables" based on Object.freeze etc) the copy/clone operation is expensive. With persistent data structures as in ImmutableJS you get faster (O(1)?) copy. Therefore, building your state store on ImmutableJS and doing an explicit comparison (if you're using React) with shouldComponentUpdate by comparing 'prevState != curState' is the only thing you need, and I had seen benchmarks that show 35% increase in performance over not using ImmutableJS.
Update:
in response to the question about having a list of items and React invoking shouldComponentUpdate, that should not be the case if React stops comparing when the parent aka the list itself says that update should not happen. Does React in that case descend down the tree to compare the state of the list items? I suppose it would but there must be a way adound it. I heard about batchUpdate add-on in this context. Researching now!
In contrast, with a fast "diff" operation, we could simply determine which of the elements in the state have changed, and this would be fast.
[1] http://eclipsesource.com/blogs/wp-content/uploads/2009/12/cl... (a random image I found on the internet that illustrates my point)
https://github.com/mquan/cortex
"Cortex is simply a store that works for updates at any level. It achieves this by utilizing cursors, which lets each nested node keep track of its path as accessed from the root. When an update occurs, the affected node emits an update event whose payload contains the path of the node as well as instructions on how to update the data at the root. Cortex's internal PubSub picks up the event and routes it to the affected root node. From there, every affected node is rewrapped and updated to maintain immutability while leaving all unaffected nodes untouched. This allows for extremely simple and efficient"
I don't know about every JS UI library, but React certainly doesn't work this way. React would indeed just see that the array object has changed and would go through every element inside. That doesn't seem to be a performance problem, even with thousands of items, although you'll probably want some pagination if your app would otherwise need to generate or change thousands of DOM elements.
[1]: https://github.com/facebook/immutable-js#javascript-first-ap...