baobab
baobab copied to clipboard
Question on Baobab's immutability
I really must be missing something, maybe someone can clue me in.
Here is what I mean:
var a = new Baobab({ b: 1 })
var c = a.set('b', 2)
a.get('b') // I expected 1 but got 2!
c // I expected a reference to the new tree but was surprised to see 2
Am I wrong to think that Baobab should work like ImmutableJS in terms of preserving the tree on mutations on just returning an updated version?
ImmutableJS doesn't have by default the excellent cursors and event system on data change like Baobab does and that's why I turned to using Baobab - but was a bit surprised that data mutated on every update.
Hello @dumconstantin. Baobab is meant to be used as store for an application state. It therefore works a bit differently than ImmutableJS. That is to say, the data stored by the tree is immutable (try setting an object and mutate this object and you'll see that's not possible). However, the tree has a internal reference to the data it is storing for you, and each time you write something to the tree, this internal reference is swapped to be replaced by the new version of the data.
You can think of Baobab as working like a Clojure atom in fact.
I was thinking lately of creating a spin off or else of the lib that would work as a plain data structure you could use like ImmutableJS but I need some time to reflect on it.
Hey @Yomguithereal, thank you for the answer! I think I got the gist now - initial data is deeply cloned and the immutability does not transpire across the API
The ImmutableJS approach is a bit easier to reason about when you want to push state around (like with React components). So the spin off might be well received.
So as a twist (I'm using RiotJS with Redux and trying to simulate Om Next/Relay ideas on listening to data change instead of passing stores around), would something like the following work?
- keeping the Baobab instance isolated
- only dispatched actions can mutate the Baobab instance
- components use fake cursors (which trigger internally the cursors on the Baobab instance) to subscribe to the Baobab's updates
- components use fake cursors to get a raw objects from Baobab
I'm just a bit worried that the Baobab instance might get somehow compromised if I don't isolate it well enough...
I think I got the gist now - initial data is deeply cloned and the immutability does not transpire across the API
Initial data is not deeply cloned. It is deeply frozen.
You can think of the tree as a Redux store in fact.
keeping the Baobab instance isolated
Yes you should
only dispatched actions can mutate the Baobab instance
Yes, only actions should be allowed to mutate the tree.
components use fake cursors (which trigger internally the cursors on the Baobab instance) to subscribe to the Baobab's updates
You should check baobab-react
components use fake cursors to get a raw objects from Baobab
What do you mean by fake cursors?
I'm just a bit worried that the Baobab instance might get somehow compromised if I don't isolate it well enough...
How could it get compromised exactly?
Thanks for the correction just saw the deepFreeze helper.
Nice, thanks confirming some ideas for the baobab-react link, yup that's kind what I had in mind with cursors.
For the "fake cursor" label, actually mean is "proxy cursors" so that you use without having the store instance
Kinda like:
import cursor from './store.js'
var name = cursor(['user', 'name'])
name.on('update', () => console.log(name.get())
For compromising, I can't ever export the Baobab instance and make sure when passing the instance to the reducers they don't store it locally or export a direct reference to it. It also mean that the main reducer and the cursor proxy code needs to stay in the same file as the Baobab instance.
Drive by comment here. My understanding is that it is inaccurate to describe a Baobab tree as immutable, even if you've frozen the underlying data. @dumconstantin's original example exactly illustrates an expected property of immutable data structures: "If an object is known to be immutable, it can be copied simply by making a copy of a reference to it instead of copying the entire object." from Wikipedia (article linked from the README). The end of that section gives the proper name for the pattern used by Baobab: Observer.
"If an object is known to be immutable, it can be copied simply by making a copy of a reference to it instead of copying the entire object."
Then immutability is impossible in JavaScript and even the immutable-js
and mori
libraries cannot achieve this goal.
@couchand, I agree that you can argue that the Baobab tree itself is not immutable. But the underlying data is immutable (or at least "not mutable" if you find the term erroneous) and persistent.
The end of that section gives the proper name for the pattern used by Baobab: Observer.
Yes, Baobab implements the Observer pattern but not only because you can very well compare two different versions of the underlying data by reference only.
Immutability is all in the eye of the beholder. The underlying data is stored on very mutable RAM, so we know that fundamentally everything is mutable, it's about the API that's exposed to the user.
Consider this snippet, the first from the ImmutableJS README:
var Immutable = require('immutable');
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
Or this one, the second example from the mori README:
var v1 = mori.vector(1,2,3);
var v2 = mori.conj(v1, 4);
v1.toString(); // => '[1 2 3]'
v2.toString(); // => '[1 2 3 4]'
The fact that these examples are so primary to the explanation of these two libraries should be a sign that they're following the expected pattern for immutable data structures. That Baobab would be described as immutable and persistent, despite that fact that the API plainly isn't, seems like a case of buzzword-compliance.
Let's not water down the meaning of the terms.
I see your point. You are right.
So would a slight rewrite of the docs & the description of the library explaining this point more clearly by correctly explaining that the underlying data is immutable but not the actual object and API (which is more like an atom or Observer) be a satisfactory solution?
That sounds right, I think the only thing that need change is the docs. Thanks!
:+1: +1