Immutable Data Collections for Javascript
github.com
github.com
Any good links showing otherwise?
Mori just has more stuff and the benefit of 3 years of development and production use. This becomes quite apparent on the metrics you asked about:
- mori is a 32K gzipped browser dependency, tons of helpers functions
- immutable-js is a 10K gzipped browser dependency
(with Uglify and aggressive mangling), mostly just data structures
ClojureScript (and thus Mori) has been continuously tuned for 3 different major JS engines - JavaScriptCore, V8, and SpiderMonkey. Running some basic benchmarks overall Mori comes out ahead on most operations. But there's nothing surprising here, immutable-js is brand spanking new :). Also there are micro-optimizations (which add up) in ClojureScript that are impossible to expose to JS users w/o a compiler.In anycase this is great stuff and I'm excited to see how JavaScript and more specifically React devs leverage these to explore the ideas currently be played around with in ClojureScript/Om/Elm/etc.
https://github.com/mattbierner/hamt/ https://github.com/Tvaroh/moreartyjs
The first implementation (hamt) is interesting because it's in a same semantics compile-to-js language (by the author) called Khepri and it benchmarks pretty well on datastructure microbenchmarks.
http://khepri-lang.com/ https://github.com/mattbierner/js-hashtrie-benchmark
The second is a less interesting pure JS implementation that I show as faster than mori and slower than hamt on the hashtrie-benchmark. I've been meaning to test them on a wider variety of benchmarks (more aggregate operations and vs transients) but I only ran across them last week and I've been busy.
Also benchmarking in Node.js doesn't reflect the wide variance you find in JS engines. In several cases in the past we avoided V8 specific optimizations because it punished other JS engines.
UPDATE: I also ran some benchmarks and I can't replicate the perf degradation as the size of the hash map increases. My suspicion is that the random key generation may result in many hash collisions w/ Murmur3 which would explain the dramatic perf degradation which is unlikely to occur in the wild.
I'm not a benchmarking or ClojureScript expert, so please let me know if you find any problems with my benchmarks or can make any improvements to make them more accurate. Hashing is one area that I need to investigate more in my libraries and in the benchmarks.
Getting more than one datapoint would be nice too. None of my code is optimized specifically for Node, so seeing benchmarks on other engines would be interesting.
I would be interested in seeing your benchmarks too. Please let me know if you find any problems or have any suggestions for HAMT or js-hashtrie-benchmarks.
I wrote an immutable vector library once, but it turned out impractical to do game physics with because it was too slow. Switching to self-modifying vectors fixed the problem. I think the performance hit of allocating new JS objects might be too high for games.
From what I've seen, I'm guessing: quite a bit, but I'm not sure that I've noticed it being mentioned. In particular, I'm guessing that the performance characteristics of Om might be especially inspiring.
Edit: Oh, and good work on the library! It's great to see efforts to make the case of immutability within the world of JS.
Regarding popularity, I feel that sometimes the web development community can be ironically dogmatic, considering that it's a free and open platform (see: HN/Reddit's initial reaction to React). Looking back, I still find it incredible that React actually gained so much traction when it defies so many of the pre-established "best practices". I think lots of people, including me, are learning more and more to take a step back before dismissing an idea.
So if you were one of those that dismissed Clojure/ClojureScript because "parentheses in front of my function call?", "snake case isn't for me", or "it's too different from idiomatic js" (I hope this one isn't too much of a strawman here), then I urge you to give it another try. Learning React doesn't have to be the only time you open your mind to new ideas and reconsider best practices.
Also, I just started getting into ClojureScript in order to learn Om. I would love to know your thoughts on why it's worth it to learn Clojure/Haskell.
[0] - http://swannodette.github.io/2014/07/30/hijacking-json/
[1] http://swannodette.github.io/mori/The guys in the ATP podcast talked about a similar gesture in the Overcast app, and that most users really enjoyed: https://dl.dropboxusercontent.com/u/19746944/overcast_others...
I don't see a reason to use any other immutable js lib when Mori exists.
I definitely need to learn more about it, as dead code elimination for JavaScript seems very, very hard due to its dynamic nature and I'm curious how they do it. Does anyone know about other JS compilers which try to perform dead code elimination?
// pseudo code
var fruitBasket = new FruitBasket('banana, 'apple)
muckWithFruits(fruitBasket)
suspiciousLogger(fruitBasket)
weirdCallback(fruitBasket)
// here
assert(fruitBasket.length == 2)
By the time we reach "here", we know that none of the functions since the inception of fruit basket have tampered with it.This may not be a big deal if you are disciplined about never modifying your source data, but this moves the guarantee from a soft one to a mechanical one.
And then there's all this typical stuff about functional programming blah blah... But that's the most practical example I can think of.
That said, I'm not sure how this applies to JS, which is single-threaded.
What I thought was the key point was this:
Let's say you have a regular JS array. There is a reference to it in memory and it has a certain value. Now you change an element in that array. You've just mutated it. In JS, the reference in memory doesn't change, but now the value of what it's pointing to has. So in order to know if the value has changed, you need to do a comparison on every element on that array, which is expensive.
Now let's say you have a immutable array (such as from the Immutable FB library or Mori). If you change an element in that array, you get a new array and a new array reference in memory. In other words, if you check that the reference in memory is the same, you are guaranteed that the value is the same (in this case, the array). So checking for equality is fast and cheap because you are only checking the reference, not each value in the array.
One way this is important in React is that now if your component state (or entire app state) is represented by an immutable data structure, you can just check for reference equality in the shouldComponentUpdate() method when deciding to re-render that component. If the reference is equal, you are guaranteed that the data backing that component hasn't changed and you can return false, telling React not to re-render.
What's surprising is that people have made these data structures also very memory efficient. You can see David Nolan talking about how they work and memory comparisons here: https://www.youtube.com/watch?v=mS264h8KGwk
I personally like the safety benefits of immutability. You can give objects to functions and not worry about the functions changing the object. The only way to guarantee that otherwise is to clone the object, but that takes quite a bit of performance.
This is important for animations where you want to animate from the object's old value to the new value.
http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immut...
Did Facebook buy the npm name from hughfdjackson, did he give it to them, or did NPM switch ownership?
https://github.com/facebook/immutable-js/blob/fdc4005fe30286...
Interesting to see references to TypeScript. Is it prevalent at FB?
But even if it is not being used yet - do we know if Facebook uses Typescript (or some extension of it)?
return nextProps.value !== this.props.value;
If the object is different, it has changed.
EDIT: Maybe I understand. You're saying a parent component might do something like: foo.bar = 'blah'; and then doing a simple equality check foo === foo is not enough, you have to check that the properties have not changed either. Is that correct?
return (nextProps.attr1 !== this.props.attr1 ||
nextProps.attr2 !== this.props.attr2 ||
nextProps.attr3 !== this.props.attr3 ||
nextProps.attr4 !== this.props.attr4);
Which would result in a lot of boilerplate code if you had to do this for every single attribute in every component in your application. It would be much simpler to just do this check: return nextProps !== this.props;
But that's impossible if you're using JSX, because the JSX compiler automatically inserts a normal JavaScript object into your code, instead of an immutable object from this library.So from my point of view this immutable-js library and JSX seem incompatible, which is kind of weird since Facebook apparently uses JSX internally for their React code.
In a flux application you typically always ask flux for the state, though, so it becomes this.getFlux().myThing.
[1] http://facebook.github.io/react/docs/pure-render-mixin.html
With C-style language structures immutable structures wouldn't be much use. With Clojure, for example, immutability is one of a few mutually complementary features of the language in which it makes perfect sense.
Immutability doesn't just mean 'doesn't change'.
Yes, isn't that enough?
The time and storage complexity of that would be exponential if each step you had to clone a copy of the output list. With a persistent data structure, as much of the structure as possible is re-used.
And a list is the simplest possible structure. What about a tree?
Using `reduce` is, I would say, in the top 5 techniques used in functional programming. And it would be severely hobbled without efficient persistent immutable structures.
Of course, what they do is efficient persistence. You want a snapshot of the data? You want a non-volatile reference? You've already got one! That's the feature. They should be called persistent data structures.
For example, a rope that reorganizes internal nodes to maximize structure sharing.
- Work on regular JavaScript Object and Array so it's easy to get started and integrate to your app
- You don't need to wrap objects inside of Immutable to use it and you can use `obj.key` and `arr[0]` syntax for accessing them instead of `obj.get('key')` and `arr.get(0)`
cons:
- There are no safe-guards if you mutate the objects and it'll likely break your app in subtle ways. This kind of discipline is very hard to get in big projects
- You are stuck with primitive data structures (instead of map hash trie), so it's not going to be as performant and memory efficient.
You also get undo/redo stacks basically for free, since you simply maintain references to the previous n immutable states.
As Om demonstrates, they also make possible significant optimizations of code that takes various view of the same underlying data, like rendering routines.