10 ms·
Immutable.js – Immutable Data Collections
- oahziur 12y agoNice!!!
- nawitus 12y agoWas this really hosted on Facebook initially? I remember this library before May, which is the date of the initial commit.
- matchu 12y agoThis was always Facebook's library, but it didn't always have a pretty homepage. You might be thinking of mori? https://github.com/swannodette/mori https://github.com/swannodette/mori
- nawitus 12y agoAh, I was thinking about "immutable.ts": https://github.com/srenault/immutable.ts https://github.com/srenault/immutable.ts
- ScottBurson 12y ago> The difference for the immutable collections is that methods which would mutate the collection, like 'push', 'set', 'unshift' or 'splice' instead return a new immutable collection. I think this is an unfortunate design decision which should be reconsidered. Functional operations should have different names than side-effecting operations. In general, I think that while side-effecting operations are commonly verbs, functional operations should be nouns or prepositions. Particularly in a language without static types, you want to be able to look at an unfamiliar piece of code and see pretty quickly what types it is using. The semantics of mutable and functional collections are similar enough that using the same names is going to be very confusing, particularly in code that uses both kinds of collection -- and such code will definitely exist. It's important that the names convey the semantics of the operation. Java's 'BigInteger' is a good example of this being done wrong -- the addition operation is called 'add', for example, and I have read that some newbies call it without assigning the return value to something, expecting it to be a side-effecting operation. I think that if it were called 'plus', such an error would be much less likely. We're used to thinking of "a + b" as an expression that returns a value, rather than an object with state. I understand that introducing new names has a cost: people have to learn them. But keeping the old names is going to drive users nuts. If you won't change the names altogether, at least append an "F" to them or something. EDITED to add: if you want some ideas, check out FSet: http://www.ergy.com/FSet.html http://www.ergy.com/FSet.html
- ninjakeyboard 12y agoNot sure if I agree - it's more natural to have only immutable collections if taking a functional approach. Eg see scala or haskell. Does strong typing change that? It's very clear if you're whole code base has no side effects what is happening when and where.
- pimlottc 12y ago> It's very clear if you're whole code base has no side effects what is happening when and where. And how likely is that? It's great if you start with a purely functional project but in most projects you're starting with a existing codebase, and you don't always have the time to go back and change everything when you figure out a better way to do it. So I don't think it would be that unusual for someone to want to use immutable objects alongside alongside "normal" mutable objects. But it would be harder to distinguish the two in code when the methods are all the same, particular when you don't have type information at hand.
- ninjakeyboard 12y agoI suppose ruby is the example where you have map! and map. I don't know - the java stream API is clear enough IMO. The Java8 approach was to allow mutating functions on collections and then have lazy-non mutative stream api. As a java8 dev, I don't think it's so hard to understand that map on a play promise is non mutative though - I'm not dropping requests for typesafe to change the api.
- baddox 12y agoI empathize with your argument that operations with significantly different behavior ought to have different names, although I think the convenience of using familiar method names might outweigh the confusion of new readers (depending on the size/scope/structure of your software project). I'm less a fan of your argument that method names like "push" or "add" inherently imply that they mutate their caller. I see no reason for that to be the case, other than in the context of your first argument. I think that an "add" method that returns a new integer without mutating its caller accurately conveys the semantics of the operation just as much as an "add" method that mutates its caller.
- jypie 12y agoWhat is the benefit of having immutable variables that are just fake-built at runtime? At compile time, knowing some variables are constant and will not change would give the interpreter/compiler a lot of optimization chances but does this apply the same at runtime as well? the website says "efficiency" because immutable variables wouldn't need to be deep copied but would the deep copy operations be that frequent in JS?
- baddox 12y agoOne benefit is that you can do simple O(1) equality comparisons in UI libraries like React to determine whether data has changed (and thus whether UI needs to be refreshed).
- taeric 12y agoThis doesn't necessarily work. Consider scenarios where you do one edit and then effectively undo it before the redraw. Or, more realistically, two rapidly successive edits effectively undo each other. Also, one could just as easily keep a dirty bit oneself.
- baddox 12y agoYeah, that's true for O(1) === comparison. The library does also provide Immutable.is(foo, bar) which does nested value checking, but that's obviously not O(1).
- moribound 12y agoI don't follow. What's wrong with effectively undoing an edit before a redraw? Why do I care about the intermediate state?
- drifkin 12y agoI think taeric is saying you "effectively undo" an edit, meaning that you could modify the state to a new state that happens to be the same as the old state (so it'll be a different object).
- ebabchick 12y agoRandom observation -- what's up with almost 20 of the 24 'contributors' to this project mostly having made 1 edit changes to the README? Is this some kind of pervasive Github resume padding scheme that I'm just now picking up on? (it will show the repo in the "Repositories contributed to" section of your profile even for just those 1-line README edits) https://github.com/facebook/immutable-js/graphs/contributors https://github.com/facebook/immutable-js/graphs/contributors
- jonahx 12y agoIt's possible, but it's also standard practice on github to submit even minor typos or spelling errors via a pull request, and that automatically makes you a contributor if accepted. So there's not necessarily anything nefarious going on.
- spicyj 12y agoAs the supposed #2 contributor to the project (with 3 commits), I'd say it's far more likely that the README is simply the most visible piece of the project (or was, before the website was released today) and people contribute back typo fixes as they encounter errors.
- leebyron 12y agoNot nefarious, but definitely something to keep in mind if you recruit via github committer lists :) I accept any reasonable pull request, even if it's a spelling fix. There have been some great bug fix pulls as well.
- malandrew 12y ago`git summary` from git-extras is great. https://github.com/tj/git-extras https://github.com/tj/git-extras
- Havvy 12y agoSo, how does this compare to Clojurescript's Mori?
- leebyron 12y agoPerformance: comparable Data-structure techniques: nearly the same API: do you like point-free functions (mori) or methods (immutable.js) mori is a direct compile of clojurescript's excellent data structures and functional tools (written in clojurescript, of course) to javascript. It favors a clojure-style API. immutable.js is entirely written in JavaScript and favors Idiomatic JS style API.
- killercup 12y agoAn important factor may also be: It's smaller in download size (15kB vs. 38kB for .min.gz).
- RoboTeddy 12y agoHere's an explanation of one of the underlying datastructures that make this library reasonably efficient: http://hypirion.com/musings/understanding-persistent-vector-pt-1 http://hypirion.com/musings/understanding-persistent-vector-...
- skrebbel 12y agoI've been using this (v2, though) rather extensively the last few months so I figured I'd share some experiences. First off, it works great. The TypeScript definition file also works fine as an API documentation of sorts, although it would be nice if some web site with a clickable TOC could be generated from it. I was a little confused at the beginning how the "Sequence" base class (changed name to Iterable in v3) was an arbitrary key=>value mapping (with no apparent relation between the keys and the ordering of the key/value pairs) that I had expected would be called "Map". The name change made this clearer for me: it's basically an abstract interface, not a concrete datastructure. The TypeScript support, to my experience, does not work as advertised on the tin, especially when you nest data structures. For example, code like this compiles fine: var q = Immutable.Map<string, Immutable.List<number>>(); q.set("hello", Immutable.List([1,2,3])); // typechecks until here q.updateIn(["hello", "wrong"], false); // shouldn't be possible, but is. Similarly, if you use Immutable.Record to build immutable objects, then you can still call set() with any key because the key is a string instead of something TypeScript could typecheck. Meanwhile, using direct property access on Immutable.Records, like this, will fail: var Banana = Immutable.Record({length: 0, colour: "yellow"}); var someBanana = new Banana(); someBanana.curve = 5.0; // should fail, but doesn't console.log(someBanana.length); // fails, but shouldn't The last line will fail to compile in TypeScript even though it works in Immutable.js. I managed to write a wrapper for Immutable.Record() that "solves" this, but at the cost of highly verbose repetition for each record class. I tried very hard to "fix" this, and I have become aware that this is nearly entirely due to limitations of TypeScript's very poor generics support. It really couldn't be any better than it is. But still, the front page claims great TypeScript support and I'd say it's half-assed at best. I chose Immutable.js over Mori because I think the API is slightly less verbose and more JavaScript-ish. I think this is mostly a matter of taste. I found that I kept forgetting the names of methods, or being unsure about their exact signatures. Also, I often use plain JS datastructures in "little" parts of the code (stuff that doesn't persist cross function calls), I use LoDash and ES5 built-ins to manipulate those, and these functions all have subtly different names and semantics again. I suspect that very few codebases won't do this in practice: mix Immutable.js data structures with native JS data structures all over the place, and a need to manipulate both here and there. I think transducers may be an opportunity there. Our very own jlongster has a nice writeup [1] of how they enable you to use the exact same data manipulation syntax for both native JS data structures and Immutable.js collections at no performance cost. I think that maybe Immutable.js should embrace this and release an alternative build of the library that has absolutely no manipulation functions except the hooks that transducers need to consume and produce Immutable.js collections. Finally, I've been doubting whether Immutable.Record is worth the hassle. Maybe a single function that can easily clone a JS object but change 1 field is more than good enough. For example, with lodash/underscore this function would be a oneliner like: var newRecord = _.merge({}, oldRecord, {someKey: newValue}); (there might be a "shorter" way, I find the underscore API horribly obtuse and difficult to figure out, but that's not the point here) The biggest disadvantage of using vanilla JS objects over Immutable.Record is that you're not sure that it'll not mutate if you forget to Object.freeze. I doubt many record-type objects have so many fields that cloning them has a larger performance overhead than Immutable.js's internals. I'm curious what other people's ideas and experiences are here. [1] http://jlongster.com/Transducers.js--A-JavaScript-Library-for-Transformation-of-Data http://jlongster.com/Transducers.js--A-JavaScript-Library-fo...
- zbyte64 12y agoI have started using this with our React.js project and have been able to squeeze out efficient DOM updates comparable to Om. For fun I also made a library that overloads Immutable.js and JSON operations (and can do the same for mori): https://github.com/zbyte64/dunderdash https://github.com/zbyte64/dunderdash
- jonahx 12y agoDoesn't React already do efficient DOM updates for you with their virtual DOM and it's diffing mechanism? Or are you referring to something else?
- WickyNilliams 12y agoReact does minimise DOM mutation, but you can get even better performance by skipping the diff step completely (via the shouldComponentUpdate hook). With an immutable data structure this method can be implemented as simple reference comparison previousData !== nextData , so you can get absolutely best perf with ease.
- bendlas 12y agoI think he is talking about implementing .shouldComponentUpdate(), which is more efficient with immutable data, since you can compare identities (===). David Nolen made this point with Om's TodoMVC.
- moribound 12y agoHow
- franciscop 12y agoI think I'm getting the example wrong. Wouldn't this: var map = Immutable.Map({a:1, b:2, c:3}); map = map.set('b', 20); map.get('b'); // 20 Give 2 instead of 20, since b is inmutable?
- rtfeldman 12y agoOnly the map variable points to an immutable object. The second line reassigns that variable to a new (also immutable) Map instance, which is an exact copy of the original except that b has been changed. Does that make sense?
- rakoo 12y agoPut further, Rich Hickey had a talk about this, I don't remember which one. Most languages (unfortunately those we usually start with) conflate _identity_ and _value_, even though they're completely different notions. It was ok with mutable structures, but immutability puts the problem on the front. A map is a value; it never changes. When you add something to a map, the old map doesn't change, only the new one does (in that it is created). We have as many values as there are transformations. Of course libraries and GCs will make sure that we use physical resources as efficiently as possible. An identity, on the other hand, links a _label_ to a _value_ at a certain point in _time_: - the label 'mymap' is first associated with the value '{"a":true}', then with the value '{"a":false}' - the label 'Tim's Birthday' is associated with the value '1970/01/13' forever (people's birthday rarely change) - the label 'President of the US' is associated with the value 'Barack Obama' for the moment, it was different a few years ago, and will be different in a few years. Going back to the example, we have a single identity, with label 'map', that takes 2 different values over time.
- rpwverheij 12y agoto put what rtfeldman said in code: var map = Immutable.Map({a:1, b:2, c:3}); var map2 = map.set('b', 20); map.get('b'); // 2 map2.get('b'); // 20
- adam23 12y ago???
- etrinh 12y agoI've been using this library for a project, and here are my thoughts on it: 1. Immutable data structures are a huge win. I can't count the number of times I've been bitten by a bug caused by some subtle mutation that happened in a part of my code that I wasn't expecting. 2. Using this library for data structures everywhere, as a replacement for native JS arrays and objects, requires you to have discipline in naming your variables so that you can infer their types. Things can get pretty frustrating when you try to map over a Set, thinking it's a Map, for example. 3. The most annoying thing might be the documentation, which consists of a type definitions file and liberal comments. It's ok, but hardly a great interface for exploring the API. Overall, liking the library so far. I think with good, searchable docs (with examples of API usage) this could be something really great.
- yawaramin 12y agoApparently they support TypeScript, which would give you types without the pain of a naming scheme....
- rpwverheij 12y ago"Subscribing to data events throughout your application [..] creates a huge overhead" So could I do without event listeners when using Immutable data? Say I have a view listening to data changes with event listeners... could I notify it about changes in data without listeners if I use immutable data?
- raspasov 12y agoThere's also this library by David Nolen which seems similar https://github.com/swannodette/mori https://github.com/swannodette/mori . Haven't used either library; someone with more experience might want to chime in on the differences/similarities between them.
- gcanti 12y agoWhat do you think about vanilla `Object.freeze`? Any drawbacks? Here my considerations at the moment: Pros: - you can use native data structures instead of continually wrap / unwrap - you can use native methods on them (map, filter, ...) - hence (almost) no interoperability problems with other libraries - overall your coding style doesn't change so much - (possible perf gains since the js engine knows the object is frozen? - I really don't know) - EDIT: 0KB dependency Cons: - you must find / write a way to update efficiently
- swannodette 12y agoLack of update is a show stopper. In general I would expect Immutable JS to trounce anything via Object.freeze with respect to performance. Real hash maps and sets are useful to organize programs - the semantics of Object.freeze on ES6 Map and Set are unclear and still suffer from above problems. Again Immutable JS has a big advantage. In the context of building real applications with immutable data structures and a library like React that makes them easy to integrate - many of your concerns don't apply at all.
- gcanti 12y ago> Lack of update is a show stopper Well, it's quite easy to write something like Facebook Immutability Helpers [1]. As you correctly stated, it really depends on your own use case and performance issues. I'll try some perf experiment with [1] or something similar to see what comes out. [1]: http://facebook.github.io/react/docs/update.html http://facebook.github.io/react/docs/update.html
- Too 12y agoLast time i checked Object.freeze actually made things slower, alot slower. Sounds counter intuitive and it might eventually change but that's the current state in most popular browsers.
- leebyron 12y agoThis is pretty unfortunate. At Facebook we use Object.freeze in a number of places to express POJSO immutability. A technique we've used to deal with the performance hit is to make most of these Object.freeze calls a no-op in a production environment. Most FB employees (incl non-devs) load the site in development mode, so we still get the stricter error messages.
- swannodette 12y agoIt's great to see more implementations of persistent data structures. I think over time people will begin to see that they allow us to radically rethink how we organize very complex programs that we have long believed are better constructed on stateful objects - user interfaces. It's been thrilling to see people discard many long held assumptions - the ClojureScript community has been running with UIs constructed on persistent data structure via Om (a layer over React) to startling effect - I highly recommend watching this recent demo from CircleCI https://m.youtube.com/watch?v=5yHFTN-_mOo https://m.youtube.com/watch?v=5yHFTN-_mOo.
- gcanti 12y agoOm is an amazing and inspiring project! thanks for writing it.
- amelius 12y agoCool video. But I don't get it really :S I just skimmed through the documentation and it says: > In return for this effort, we get powerful debugging tools Is this the primary purpose of this tool? Or is it the undo feature?
- infinity0 12y agoHi! We would dearly love to use this in our code (a MEGA webclient subproject) but it's 51KB! Do you have ways to build only parts of this so that I can include (e.g.) only Immutable.Set? At the moment I'm using a custom immutable wrapper around a ES6 Set polyfill.
- leebyron 12y agoYes, please open a github issue about this. There are some non trivial changes needed to make this possible, but this is something I would love to enable in a future version. Also, if you're minifying and gzipping your static resources (standard practice these days), then Immutable.js is a relatively fit 15KB. For comparison, lodash is 8KB and jQuery is 30KB. I definitely understand the desire to not include code that will never run (we pain to optimize this at FB), but at least it won't break the proverbial bandwidth bank.
- saurabhnanda 12y agoIs it possible to use this as a drop-in replacement for Angular POJOs that hold $scope data? We're hitting a whole class of bugs which are basically due to mutability of native JS objects, and we could use something like this for the most complex ng-controllers.
- likeclockwork 12y agoObject.freeze, maybe? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- leebyron 12y agoI've never tried this myself, but anything is possible. If you get this working, and want to write a blog post about it, let me know!
- malandrew 12y agoWhat are the implications on memory usage in a long running app with something like immutable.js? For example, let's say we have a mercury app [0], and the state of the app is based on a persistent immutable data type. It would seem to mean that so long as the state of the app doesn't change much that everything would work just fine because there total number of diffs is minimal. However, what happens in the case of an app where there are event sources that produce lots of data (mouse movements for example) and therefore can result in lots of diffs. Wouldn't this be a source of memory usage that just keeps creeping up and for which memory is never released? [0] https://github.com/Raynos/mercury https://github.com/Raynos/mercury
- leebyron 12y agoThere should not be linearly increasing memory usage. If you find memory leaks like this, please file bugs on github issues. AFAIK there currently aren't any. Of course, if you do something like keep an undo stack around (check out swannodette's blog for a great example of this) then of course you have linearly increasing memory usage. One great property of persistent data structures is that the memory usage will be much smaller than if you kept your undo stack as copied JS values because of structural sharing (see one of swannodette's recent talks for a great explanation of this). In practice, if you're implementing an undo stack, you should remain conscious of memory usage. You may want to only keep a fixed maximum number of previous states around so you don't have linearly increasing memory use.
- malandrew 12y agoDo you know of a way to limit your undo stack not in terms of undos (snapshots) but in terms of memory usage? i.e. limit the undo stack to N megabytes of memory? Furthermore, do you know of any ways to perform automated testing for memory leaks?
- leebyron 12y agoIn production, no. In a development environment yes. AFAIK, there is no exposed API in the browser to describe the size of the heap. However, you can expose hooks to get this information in a v8 or node instance; snap heap size, run tests, run GC, snap heap. Compare before and after in your test runner.
- amelius 12y ago> Immutable always returns itself when a mutation results in an identical collection, allowing for using === equality to determine if something has changed. But any smart equality-testing function would already include this.