Imagine a situation that John have two childrens Alice and Bob, and Bob have a cat Orion.
var Immutable = require('immutable');
var parent = Immutable.Map({name: 'John'});
var childrens = Immutable.List([
Immutable.Map({name: 'Alice', parent: parent}),
Immutable.Map({name: 'Bob', parent: parent})
]);
var cat = Immutable.Map({name: 'Orion', owner: childrens.get(1)});
After few years John wants to rename to Jane.
var renamedParent = parent.set('name', 'Jane');
...and let childrens know about it.
childrens = childrens.map(function(children) {
children.set('parent', renamedParent);
});
Then I have to update cat because Bob changed.
cat = cat.set('owner', childrens.get(1));
Is it possible to automatically update all related objects when one object change? I looked at Cursors, but I'm not sure if they are a solution. If it possible, can you give me an example?
Paraphrasing the question:
Is it possible to automatically update all related objects when one object changes in an immutable collection?
Short answer
No.
Long answer
No, but nothing ever changes in an immutable data structure so that's not a problem.
Even longer answer
It's more complicated...
Immutability
The whole point of immutable objects is that if you have a reference to an immutable object you don't ever have to bother checking whether any of its properties have changed. So, is it a non-issue? Well...
Consequences
There are some consequences of that - whether they are good or bad depends on your expectations:
- There is no difference between pass-by-value and pass-by-reference semantics
- Some comparisons can be easier
- When you pass a reference to an object somewhere you don't have to worry that some other part of code will change it
- When you get a reference to an object from somewhere you know it will never change
- You avoid some problems with concurrency because there is no notion of a change in time
- When nothing ever changes you don't have to worry whether changes are atomic
- It's easier to implement software transactional memory (STM) with immutable data structures
But the world is mutable
Of course in practice we often deal with values that change in time. It may seem that immutable state can't describe mutable world but there are some ways people deal with it.
Look at it this way: if you have your address on some ID and you move to a different address, that ID should be changed to be consistent with new true data, because it is no longer true that you live at that address. But when you get an invoice when you buy something which contains your address and then you change your address, the invoice stays the same because it is still true that you lived at that address when the invoice was written. Some data representations in the real world are immutable, like invoices in that example, and some are mutable like IDs.
Now taking your example, if you choose to use immutable structures to model your data, you have to think about it in a way that you think about the invoice from my example. It may be true that the data is not up to date but it will always be consistent and true for some point in time, and it will never change.
How to deal with change
So how to model change with immutable data? There is a nice way it has been solved in Clojure using Vars, Refs, Atoms, and Agents, and ClojureScript (a compiler for Clojure that targets JavaScript) supports some of them (in particular Atoms are supposed to work as in Clojure but there are no Refs, STM, Vars or Agents - see what are the differences between ClojureScript and Clojure regarding concurrency features).
Looking at how Atoms are implemented in ClojureScript it seems that you can just use ordinary JavaScript objects to achieve the same. It will work for things like having a JavaScript object that is itself mutable, but it has a property that is a reference to an immutable object - you will not be able to change any properties of that immutable object, but you will be able to construct a different immutable object, and swap the old one to the new one in your top-level mutable object.
Other languages like Haskell that are purely functional may have different ways to deal with mutable world, like monads (a concept notoriously hard to explain - Douglas Crockford, author of JavaScript: The Good Parts and discoverer of JSON attributes it to "the monadic curse" in his talk Monads and Gonads).
Your question seems simple but the problem it touches is actually quite complicated. Of course it would be missing the point to just answer "No" to your question whether it is possible to automatically update all related objects when one object changes, but it is more complicated than that, and saying that in immutable objects nothing ever changes (so this problem never happens) will be equally unhelpful.
Possible solutions
You can have a top level object or variable from which you always access all your structures. Let's say you have:
var data = { value: Immutable.Map({...}) }
If you always access your data using data.value
(or with some better names) then you can pass the data
to some other part of your code, and whenever your state changes you can just assign a new Immutable.Map({...}) to your data.value
and at that point all your code that uses data
will get fresh values.
How and when to update the data.value
to a new immutable structure could be solved by making it automatically triggered from your setter functions that you would use to update your state.
Another way would be to use similar tricks at the level of every structure, for example - I use the original spelling of the variables:
var parent = {data: Immutable.Map({name: 'John'}) };
var childrens = {data: Immutable.List([
Immutable.Map({name: 'Alice', parent: parent}),
Immutable.Map({name: 'Bob', parent: parent})
])};
but then you have to remember that what you have as values are not immutable structures but rather those additional objects with references to immutable structures that introduce an additional level of indirection.
Some reading
What I would suggest is to look at some of those projects and articles:
- Immutable React article by Peter Hausel
- Morearty.js - using immutable state in React like in Om but written in pure JavaScript
- react-cursor - functional state management abstraction for use with Facebook
- Omniscient - a library providing an abstraction for React components that allows for fast top-down rendering embracing immutable data * Om - ClojureScript interface to React
- mori - a library for using ClojureScript's persistent data structures in JavaScript
- Fluxy - an implementation of Facebook's Flux archtecture
- Facebook's Immutable - persistent data collections for JavaScript that when combined with Facebook React and Facebook Flux faces similar problems that you have (searching for how to combine Immutable with React and Flux may give you some good ideas)
I hope this answer even if not giving a simple solution will nevertheless be helpful, because describing mutable world with immutable structures is a very interesting and important problem.
Other approaches
For a different approach to immutability, with immutable objects that are proxies to data that is not necessarily constant, see the Immutable Objects vs. Common Sense webinar by Yegor Bugayenko and his articles:
- Objects Should Be Immutable
- How Immutability Helps
- How an Immutable Object Can Have State and Behavior?
- Immutable Objects Are Not Dumb
Yegor Bugayenko uses the term "immutable" in a slightly different sense than what it usually means in the context of functional programming. Instead of using immutable or persistent data structures and functional programming, he advocates the use of object oriented programming in the original sense of the term in a way that you never actually mutate any object but you can ask it to change some state, or mutate some data, that is itself considered to be separate from the object. It's easy to imagine an immutable object that talks to a relational database. The object itself can be immutable but it can still update the data stored in the database. It's somewhat harder to imagine that some data stored in RAM can be thought of as equally separate from the object as the data in the database but there is really not much difference. If you think about objects as autonomous entities that expose some behavior and you respect their abstraction boundaries then it actually makes a lot of sense to think about objects as something different than just data and then you can have immutable objects with mutable data.
Please comment if anything needs some clarification.
As rsp clarified, this problem reflects the inherent trade-off offered by immutability.
Here's an example react/flux-ish app demonstrating one way to deal with this problem. The key here is that numeric IDs are used for reference rather than Javascript references.
From what I understand, you might be using immutable objects a bit too high level. As far as I know immutable objects are useful to represent time-captured states. So it's easy to compare state1 === state2. In your example however, you have also time-captured relationships in the state. Which is fine to compare the complete state with another complete state, because it's unchanging data so it's very readable, but therefor also hardly updatable. I suggest before you add another library to fix this problem, try and see if you can implement Immutable at a lower level, where you actually NEED to compare time-captured states. For example, to know whether 1 entity (person/cat) changed.
So where you do want to use immutable objects, use them. But if you want dynamic objects, don't use immutable objects.
It might be that this situation can be solved by using ids
to reference the parents rather than parent records themselves. I'm looking to SQL (sorta) for guidance here: A record should not have a pointer to another record in memory, but rather should know the id
of the other record.
In my apps I like having each object only know the primary keys of its reference objects so I don't have to worry about questions like what if an object changes. As long as its key stays the same (as it should) I can always find it:
var Immutable = require('immutable');
var people = Immutable.OrderedMap({
0: Immutable.Map({name: 'John', id: '0'}),
1: Immutable.Map({name: 'Alice', id: '1', parentId: '0'}),
2: Immutable.Map({name: 'Bob', id: '2', parentId: '0'})
});
var cat = Immutable.Map({name: 'Orion', ownerId: '2'});
Now I can change any characteristic of any record (except the id
of course) using the standard immutable.js
tools without having to do any additional updating.
people = people.set('0', people.get('0').set('name', 'Jane'))
After being frustrated and getting no where with this exact same problem, I decided to move relationships out of my objects completely and manage the logic of various common associations (one-to-one, one-to-many, many-to-many) separately.
Essentially, I'm managing relationships with bi-directional maps and keeping relationship logic centralized in an object instead of in each of my modeled data as is traditionally the case. That means I needed to come up with the logic to observe, for example, when a parent has changed and propagate the changes to the child relationships (hence the bimaps). Since its abstracted in its own module however, it can be reused to model future relationships.
I decided not go with ImmutableJS for handling this, instead rolling my own library (https://github.com/jameslk/relatedjs) and using ES6 features, but I imagine it can be done in a similar way.
来源:https://stackoverflow.com/questions/27446960/immutable-js-relationships