Immer is a library of persistent and immutable data structures written in C++
github.com
github.com
https://github.com/immerjs/immer
Looks like the C++ one started first (2016-05 vs 2017-12).
OT but it’s worth reading the code for immerjs. It’s super small, super clever and shows the power of proxies in js.
(For reference, I'm a Redux maintainer. We now recommend Immer as the best way to write immutable update logic in Redux apps, and use it in our Redux Starter kit package.)
Here's the current roadmap towards 1.0:
https://github.com/reduxjs/redux-starter-kit/issues/82
I just pushed out a breaking set of changes a few days ago, and will likely make one more set of breaking changes to `createSlice` in the near future. After that, I'm hoping the API will _mostly_ be good to go, and I can push it towards 1.0 in the very near future.
Do you know some reference that addresses this?
And if we're here an additional question: since our store data is currently composed of deep immutable JS data structures, do you know of a way to slowly migrate to Immer?
I've tried to move parts of the store to Immer, but since the root store obejcts are still Immutable js objects, this didn't go well.
All that's necessary is to do immutable updates of your data, by always making _copies_ of the data and modifying the copies (see [0] and [1] for discussions of immutable updates).
It doesn't matter whether the new object references were produced by manually creating copies ( `{...obj}`, `someArray.map()`, etc), by using Immutable.js's API, or Immer tracking the "mutations" via a proxy and creating the new references internally. As long as you have immutable updates and new references, shallow comparisons work fine.
So, there's nothing special you need to do to make Immer work with comparisons. You literally just use it as described in the docs.
Specifically for comparisons, `PureComponent` and `React.memo()` both do shallow comparisons of `props` by default.
Yes, the pervasiveness of Immutable.js's API is one of the reasons I've long suggested that folks avoid it [2]. I don't have any detailed suggestions for migrating, other than perhaps consistently using Reselect selectors to encapsulate reading values from the store and handling the Immutable.js -> `toJS()` conversion (which you should probably be doing anyway as a perf optimization, since that conversion is relatively "expensive"). That way, as you switch over chunks of reducer logic from Immutable.js to Immer, the code consuming those values _should_ still be getting POJOs out of the selectors, with no actual visible change.
[0] https://redux.js.org/recipes/structuring-reducers/immutable-...
[1] https://daveceddia.com/react-redux-immutability-guide/
[2] https://www.reddit.com/r/javascript/comments/4rcqpx/dan_abra...
We recently switched from using ImmutableJS in our Redux store to using ImmerJS.
We took the approach one reducer at a time. We have multiple reducers and we're using combineReducers (the ImmutableJS one until it was completely converted).
We had no issues whatsoever in the change. ImmutableJS doesn't care about plain JS objects (which is what ImmerJS reducers return) so everything worked as expected.
React's shallow comparison worked as expected since ImmerJS produces new objects on mutation (just like ImmutableJS).
Essentially: there is no case where ImmutableJS is needed. All functionality and benefits are available in ImmerJS which is a much easier to use library.
To do the migration, we literally just used the docs on the Github repo. This was back with version 1.0 or something and it wasn't documented very well but we were able to figure it out. The first code example is how to use it with Redux. Also the section on Currying is useful.
Edit to add: This is all within the context of web apps, specifically React apps with Redux. I'm sure there's probably some places where ImmutableJS is desirable, but I don't know of any.
Immerjs just improves the ergonomics updating a native nested structure in a way that the original contents are maintained as much as possible. Because js has dreadful support for equality checks, this definitely comes in useful.
With Immer and "hand-written" immutable updates, doing a `{...obj}` or `someArray.slice()` will do a "shallow" copy of the object. This is equivalent to what Immutable.js does in that it keeps all the fields pointing to the same references. However, the JS engine does have to do an O(n) processing of all the individual top-level keys, whereas Immutable.js's internal data structure is O(logN) or something like that. So, for _very_ large objects, it is faster for Immutable.js to make a copy than it is for plain JS data.
My take, however, is that
- Most apps will not be dealing with gigantic objects, so there's no perf benefit from using Immutable.js
- Immutable.js is a much larger dependency than Immer
- The fact that Immer works with POJOs _and_ gives you the ability to write trivial "mutative" syntax for updates is a huge improvement over Immutable.js's Java-inspired API
So, to me, the only hypothetical use case where Immutable.js would actually be worth it is if your app is consistently dealing with huge objects, _and_ there is truly serious overhead from copying them that needs to be optimized.
The linked document only mentions high-level examples, all of which seem to be trivially implementable via mutable data structures and a bit of being careful.
Does this buy anything beyond peace of mind that if you pass an immutable DS to another thread, they won't be able to change it?
Sean Parent has a conference video out there about how undo in Photoshop is implemented by breaking the image into tiles then making a list of the history of each tile. This allows the “History Brush” to reach into the past and paint old pixels over new ones.
Pretty impressive.
Here's a document[1] explaining how to make a tree-like data structures persistent along with a motivating example of their use (at the end). The wikipedia page[2] for the problem also goes into other techniques and has some diagrams too.
tl;dr a naive approach to the point location query problem achieves O(N^2) space and O(logN) time per query, while simply modifying the data structure to be persistent achieves O(N) space while retaining O(logN) time per query.
[1] https://saco-evaluator.org.za/presentations/camp3_2017/persi... [2] https://en.wikipedia.org/wiki/Point_location
The same simplicity applies analogously to various other use cases which would be tedious and error prone to implement without immutable data structures.
However, the copying becomes impractical from a performance point of view if the resources are big, so immutable resources offer an alternative that does not have the performance issues.
For example, if you have multiple threads computing sub-graphs of a graph and passing those sub-graphs around to other threads, copying the whole graph each time a thread needs to compute something that may potentially alter the graph is a killer for performance. You then have two choices:
Choice 1: make the graph thread-safe. Which is the usual route C++ takes.
Choice 2: make the graph out of immutable data structures.
Choice 1 is the practical choice when the scope of the work is limited, and therefore thread safety is easy to implement.
When the problem at hand becomes very complex though, and it's difficult to reason about the ordering of locks, immutable data structures is an easier choice.
The data structures are used during program state exploration because they are more memory efficient than storing many copies of the program state with small mutations.
If this construct, that I actually never needed in my entire career, is handy for undo/redo, why not just have a dead simple undo stack? In what situation is an immutable structure your best choice?
x.add(y);
x.plus(y);
People have been known to write the first one expecting 'x' to be updated. With the second, I think it's clearer that it's a value that needs to be assigned to a variable.For the operation in question here, I suggest 'with'. This is what I have used in my own functional collections libraries; I think it originated in SETL.
x.add(y); // x now equals x + y
x.adding(y); // returns x + y without mutating xThe algorithms used in these analyses are usually described in terms of mathematical objects which are inherently immutable and using immutable objects makes correctness proofs easier.
When writing a piece of code, I start with immutable data structures because of the ease of mind they bring to the table (you can pass them around and you know they will not be modified so it makes reasoning and debugging easier, this is less of a problem in C++ because it has const references and const methods). Then, if an algorithm needs a mutable data structure or if the immutable data structures are too slow (e.g. the code is in the hot path) then I switch to mutable data structures. I think it is just a different mentality when approaching programming. It works for me because (a) it is the norm in my field (program analysis, or programming languages in general), and (b) I use a programming language with an ecosystem around immutable data structures (Scala).
In the OOP world, you might have a "getter" for a property (e.g. a list of some kind) that you don't want readers to be able to modify.
Perhaps "set" was a poor name here, since it usually means something else in a C++ context. I quite like "with".
It may or may not be implemented this way.
This is extremely useful for undo/redo, it basically comes for free with it. You no longer have to deal with invisible mutation, listener, event, you just generate the ui on each change from the base to the leaf, and everything is always correct for free. The advantage over an undo stack is that there is a re-utilization of unchanged intermediate data structure (what is change is only the path from the base to the changed element), so you can add a simple ref check in the ui to not redraw of the underlying data has not changed. So the perf come for free.
auto const &lol = make_wtf();This allows you to force an object to be immutable at a compiler enforced level regardless of how the calling code tries to initialize it.
> What I have specifically is a class that provides a base set of operations and a subclass that lets you do some modifications
If you actually do this with public inheritance then you possibly break const correctness.
Having to separate types for the immutable and mutable data structures with appropriate conversions is a sound idea though.
Your object isn't actually immutable, it just doesn't let you directly mutate it. That's a really important distinction.
That's a common pattern. In C++, rather than using a subclass for the mutator, use a different (friend) class. The semantics for implicit conversion between two different classes will allow you to accomplish what you are asking for with enforcing no changes from one interface but not the other.
Unfortunately I don't control this class :(