Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Immutable.js relationships

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?

like image 674
user1518183 Avatar asked Dec 12 '14 15:12

user1518183


People also ask

What are immutable in JavaScript?

Immutables are the objects whose state cannot be changed once the object is created. Strings and Numbers are Immutable.

What is an example of an immutable object in JavaScript?

The object being frozen is said to be immutable because the entire object state (values and references to other objects) within the whole object is fixed. Note that strings, numbers, and booleans are always immutable and that Functions and Arrays are objects.

Should I use immutable JS?

Using ImmutableJS can improve dramatically the performance of your application. And, because the immutable data never changes, you can always expect new data to be passed from the above. To make sure you are correctly comparing the data and not updating the UI when there is no change, you should always use the .

Are strings in js immutable?

String specifications among programming languages vary, however most languages treat them as reference types. But strings in JavaScript are different. They are immutable primitives. This means that the characters within them may not be changed and that any operations on strings actually create new strings.


2 Answers

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.

like image 116
rsp Avatar answered Sep 19 '22 04:09

rsp


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.

like image 29
alden Avatar answered Sep 21 '22 04:09

alden