5 ms·
Immutable Data Structures That Are Compatible with Normal JS Arrays and Objects
- dsun180 9y agoThis looks great. Do you know the exact reason why ie10/11 is not supported?
- filipn 9y agoIt requires ES5 features like Object.freeze and Object.defineProperty to work correctly. https://github.com/es-shims/es5-shim#may-fail https://github.com/es-shims/es5-shim#may-fail
- kasbah 9y agoI believe it actually supports IE9+ but the tests are currently failing so they are marked red in the test array. I suspect that they are only failing for the development branch and everything works with IE9+ if you use the latest release.
- deegles 9y agoAny good primers on why it's beneficial to use immutables in JS? (especially with server side dev)
- kasbah 9y agoNot sure what you mean by primer but I prefer using immutable data structures because it eases programming in a functional style. I use Redux for server-side as well as front-end projects but using Redux without immutable data structures is a PITA because you have to constantly think about whether you are mutating things or not. I have been using immutable.js a lot but the difference from standard JS data types is kind of cumbersome and can become annoying so this project looks really neat. I am looking forward to giving it a try.
- tommoor 9y agoTwo reasons: - Clarity of code. You can't accidentally mutate data that will then be used elsewhere. - Performance. Being able to avoid deep equality checks makes a huge difference for React in particular.
- pault 9y agoIn general hand-wavy terms, it helps you write side-effect free functions; if you are operating on a data structure defined outside of a function's scope and mutate it directly, you lose reproducibility and your code becomes very difficult to test. By ensuring that the data structures are immutable, you remove one common source of unpredictable behavior. You can be confident that no matter how many times you pass a function a particular state, the resulting state will always be the same. You can do this without immutable data structures by being very careful and knowing exactly which operations mutate and which return new structures (you'll see JS devs using `Object.assign` and `Array.concat` for this purpose), but enforcing immutability with a library takes the burden off the individual developer. One of the reasons immutable JS data structures have been gaining in popularity the last few years is the adoption of React; the virtual DOM diffing algorithm relies on a few state management methods for knowing which nodes need redrawing and the DOM won't update correctly if you directly mutate the data structures the components are using for their state. As for server-side, I can only assume it's the general trend of JS developers increasingly adopting a more functional style. One of the nice side effects (hah) of using immutables the ability to easily keep a history of state transitions and revert to a previous state if something goes wrong. This can be particularly useful when dealing with distributed systems. It's also much easier to reproduce an undesired state when debugging production issues. It's not a silver bullet but it reduces the number of heisenbugs that turn up in production.
- acemarke 9y agoTo add a bit more context: React itself doesn't care about mutations in terms of re-rendering and virtual DOM diffing. In fact, it doesn't even care about mutations in terms of calling `setState()`. I'll recap a couple previous comments of mine at https://news.ycombinator.com/item?id=14706862 https://news.ycombinator.com/item?id=14706862. You can mutate a component's state and then call `setState()`, and React will still update the component as you'd generally expect. For React, immutability primarily relates to implementing `shouldComponentUpdate` using the standard shallow equality comparison approach for the best performance, and general use of Functional Programming principles for avoiding side effects. For Redux, immutability matters for ensuring that reducer functions are pure with no side effects, as that is a requirement for time travel debugging and proper updates of Redux-connected components. So, overall immutability is definitely an encouraged approach in the React/Redux ecosystem, and there's specific places where that immutability is important for desired behavior.
- always_good 9y agoA hard part of React is determining when and what to re-render. It's one of the main things the state-management tools solve (Redux, MobX, etc). For example, imagine the naive example where you just have a global `const store = { ... }` and all components read from it and update it. You could tell everything to re-render on any change, but that's slow. So you can then implement `shouldComponentUpdate` for each component to manually check if their dependent data changed. But that may be a lot of work where each component needs to know quite a bit about the data. For instance, how do you check if two arrays are the same and without walking both arrays? Depending on your data, it may be as simple as comparing `item.id` of the first item in each array. But it might not be. On the other hand, you can tell if two immutable datastructures are equal with reference equality (obj1 === obj2). If all of your components take immutable data, then you can abstract away `shouldComponentUpdate` with a reference comparison. This is what React's PureComponent does. That's the main use-case. Not as many obvious use-cases on the server side in Javascript.
- kasbah 9y agoseamless-immutable-diff and seamless-immutable-cursor look like neat add ons for this. https://github.com/micnews/seamless-immutable-diff https://github.com/micnews/seamless-immutable-diff https://github.com/MartinSnyder/seamless-immutable-cursor https://github.com/MartinSnyder/seamless-immutable-cursor
- tommoor 9y agoI've been using this library for over a year, I'd say if you have an existing project that you'd like to use Immutable objects in then it's a great choice with minimal downsides. For a greenfield project Immutable.js is probably a better bet as the newer data structures come with a lot of benefits.
- kasbah 9y agoI have just started a React-Redux project using Immutable.js and am considering switching it over to this as I can see a lot of advantages in the compatibility with JS data. What downsides do you think I would encounter after switching?
- tommoor 9y agoThe main thing I can think of is that it will add all it's internal methods to your objects, which restricts which properties you can use – eg, "update", "merge" etc. This isn't usually an issue but we happened to have some policy objects with the same property names. It also doesn't play nicely with Flow unless you use the static syntax.
- kasbah 9y agoAh, wasn't aware. Good to know. Well, I never did get Immutable.js to play nicely with Flow which is kind of why I gave up on Flow. I wondered if you might have something to say on performance issues. You didn't encounter anything of that sort?
- Y7ZCQtNo39 9y agoI've been using this: https://github.com/guigrpa/timm https://github.com/guigrpa/timm The onus is on the developer to actually ensure immutability, but as long you're always mutating via the library functions, you should be fine. I've used more serious solutions, like Immutable.js, but I prefer this instead. The wrapper functions in Immutable.js, in my experience, aren't worth the trouble.
- kasbah 9y agoWell if the onus is on the developer it kind of defeats the purpose no? As you likely know seemless-immutable also has a "production" mode that has better performance but doesn't ensure it. That seems like a good compromise to me, so that at least during development you get errors when things are being mutated. The benchmarks in the Timm readme are interesting though. I would like to see a benchmark of seamless-immutable in production mode added to compare.
- Y7ZCQtNo39 9y agoIt doesn't defeat the purpose. It only mutates if there is actually a change to the data structure. When used properly, timm works great. The (potential) problem is that it is still possible to mutate objects without the use of timm's utility functions. If you value the performance benefits (no penalty for freezing objects since timm doesn't do that), and understand that to get the benefits, you must only mutate objects using timm, choose timm. If you value the additional safety other libraries provide, then absolutely use one of those. It all depends on the use case. You don't need to freeze objects if you are doing it properly. You could even do static analysis to know (by checking to see if you're reassigning object properties, etc).
- acemarke 9y agoThere's generally three categories of immutable data libraries for JS: - Purpose-built specialized data structures (Immutable.js, Mori) - Libraries that use freezing in some way - Utilities that abstract over immutably updating plain JS objects and arrays. seamless-immutable falls into the second category. Looks like it also adds some extra methods to objects it wraps/returns, and overwrites mutating methods to throw errors to help you avoid them. My Redux addons catalog has a large page listing all of the libs I've seen that fall into these categories [0]. If you don't want to use one of the specialized data structure libs or libs that do freezing, there's at least a couple dozen immutable update utility libs out there, with a variety of APIs to choose from. My list also has a section of Redux middlewares that will help debug accidental mutations in development, either via freezing or other comparisons [1]. [0] https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md https://github.com/markerikson/redux-ecosystem-links/blob/ma... [1] https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md#linting https://github.com/markerikson/redux-ecosystem-links/blob/ma...
- hex13 9y agoI've build a library Transmutable which falls into the third category :) it allows to use immutable data structures in mutable-like way, using plain assignments: https://www.npmjs.com/package/transmutable https://www.npmjs.com/package/transmutable (mutations are not performed but they are just recorded in ES6 Proxy, and object is cloned on commit (sort of copy-on-write), and mutations are then applied). So it basically enables for writing such things: const copy = transform(original, stage => { stage.x = 10; stage.y = 20; stage.foo.bar = 123; }) instead of Object.assign / ... mess.
- acemarke 9y agoNice, I hadn't seen that yet. I'll have to take a look at it later.
- coltonv 9y agoThis is a neat idea, hadn't thought of this but it could definitely help me bring new developers into the immutable style more easily. The only issue with it is, of course, the fact that Proxies are ES6 and everyone still has to deal with old browsers. I know MobX does similar object observation, and they support IE, but I've never had the time to go into their codebase. Do you know if something like this could be written in a way that allows IE10/11 to support it? Also, could you elaborate on why it's necessary to use a proxy at all, when in reality you could just do a copy of the object from the very beginning and allow them to do mutations on that?
- SirensOfTitan 9y agoDoes `Object.freeze` still incur a performance penalty? Static enforcement of immutability is possible today for array types using something like `$ReadOnlyArray` tagging in flow, if a bit clunky. I think a light wrapper that: 1. Integrated into flow and typescript. 2. Rewrote mutable methods like push/splice as immutable. ... would be sufficient for many needs. I love Immutable, but it does incur a performance penalty for the expressiveness (as far as I know), and works best when starting a new project over integrating into existing (where to/from array becomes more of an issue).
- zdragnar 9y agoMost libraries have flags for an environment variable to skip freezing in "production" mode; any decent minifier will remove the conditions since they can't be reached, so there's no performance penalty. Tcomb does this, and is far and away one of my favorite JS libraries for all of the other features it provides as a result. Edit: to be clear, tcomb provides runtime types, algebraic data concepts, pattern matching, validation, and with plugins react form generation and other things. Immutability is a simple consequence of needing to guarantee a valid value stays valid; it's not so much a "feature" per se
- iorekz 9y agoIs there perf gain when comparing 2 immutable obj deeply with === ?
- kasbah 9y agoIt appears equality checks for objects is not supported i.e.: Immutable({a: 1}) !== Immutable({a: 1}) There is no built in equality function either [1] so that seems like that's a big advantage Immutable.js has over this. Maybe seamless-immutable-diff [2] could be a way to do it but I am not sure on the performance. [1]: https://github.com/rtfeldman/seamless-immutable/issues/186 https://github.com/rtfeldman/seamless-immutable/issues/186 [2]: https://www.npmjs.com/package/seamless-immutable-diff https://www.npmjs.com/package/seamless-immutable-diff
- acemarke 9y agoA `===` comparison is explicitly _not_ a deep comparison. It's a reference comparison, ie, "are these two variables pointing to the same object in memory?". Under the hood, that probably is interpreted as a simple pointer comparison, which is fast and cheap. That's why it's the preferred approach for doing comparisons in cases like this. _If_ you update your data immutably, then you get the benefit of quick and simple comparisons to see if things are different. Now, a reference comparison only tells you if the objects themselves are different. It's entirely possible to have two different objects with the same or equivalent contents, such as in @kasbah's example. Both objects have a key named `a` that has a value of 1, but the objects are different references. The overall assumption when you update data immutably and compare like this is that _if_ two objects are different references, then they _probably_ have different contents, and it's time to re-render your UI.
- vbezhenar 9y agoModern JavaScript is full of syntax sugar but lacks operator overloading which would be really useful for custom data structures. This surprising me.
- deleted 9y ago[deleted]
- setzer22 9y agoThere is a huge loss in usefulness for immutable objects when immutability is not a default and you have that many different implementations. The thing I like about immutability in languages that implement it by design is that you know that if you get an data structure, it is immutable. In the world of javascript, you not only have to worry about whether an object you get from a library is immutable, but which kind of immutable implementation is using. Moreover, that information is not easily available in the type signatures, so you have to go to the library author's documentation, if any! That, to me, is a complete mess.
- Rotareti 9y agoI totally agree with you. I wish we had a built-in immutable array type and a built-in immutable object type. I love the way it's done in Python, where you have Lists vs Tuples and Class Objects vs NamedTuples. You can consume them the same way: my_list = [1,2,3] # mutable my_tuple = (1,2,3) # immutable my_clsobj = myclass(foo=1, bar=2) # mutable my_namedt = mynamedt(foo=1, bar=2) # immutable my_list[0] # 1 my_tuple[0] # 1 my_clsobj.foo # 1 my_namedt.foo # 1
- macintux 9y agoMore generally, this is why I don't find languages that add "functional" features very compelling. Behavior is so well-defined in a functional system that it's intensely frustrating dealing with any imperative/OO language.
- tekkk 9y agoNice! I will try it out as soon as I can but if it rids me of Immutable.js I will be so happy. Its API is so convoluted and you have to use .toJS() all the time since using get() is so clunky and while transforming value back to JS it will fail if it was undefined. Many small things that succeed in killing the joy of immutability for me. (But mostly the huge API that requires way too much studying) Just a quick question is there a helper library for connecting immutable redux-store to localStorage? That is something I dearly miss if there is none although you could write one yourself I guess. There is also no chaining of method calls or withMutations equivalent. Granted for small stuff not that important but it would be nice to have them in toolbox if use case arises. But thanks. Immutability in JS is such a pain which will hopefully change in the future. Until then I'm looking for the next best thing.