ecmascript-immutable-data-structures
ecmascript-immutable-data-structures copied to clipboard
Difference with Object.freeze
Can you explain: how new record:
const xy = #{ x: 1, y: 2 }; // frozen value type
const xyz = #{ ...xy, z: 3 }; // functional extension
is different from:
const xy = Object.freeze({ x: 1, y: 2 });
const xyz = Object.freeze({ ...xy, z: 3 });
?
And how new tuple:
const xy = #[ x, y ]; // frozen value type
const xyz = #[ ...xy, z ]; // functional extension
is different from this:
const xy = Object.freeze([ x, y ]); // frozen value type
const xyz = Object.freeze([ ...xy, z ]); // functional extension
?
If they're not deep immutable, it's looks like there are no differences. I suspect new types have some performance gains, but don't understand how it can be achieved.
A couple of things from my understanding (might be wrong), mostly assuming the implementation in JS engines would be something like Immutablejs. There are probably more reasons that I'm unaware of.
- Value equality vs reference equality
const x = Object.freeze([ 1, 2 ]);
const y = Object.freeze([ 1, 2 ]);
console.log(x === y); // false
vs
const x = #[ 1, 2 ];
const y = #[ 1, 2 ];
console.log(x === y); // true
- You can get additional memory optimizations in the implementation through structural sharing. In the first example above you have two arrays allocated. In the second example they are literally the same object. Lee Byron does a good job of explaining this in his ReactjsConf talk (start at 5 minutes)
- There is a cost to freezing objects. In the product I work on we recursively freeze all of the data while in dev mode, and it results in a considerable drop in performance vs prod where we don't freeze the objects.
- There is some wonkiness when working with frozen objects with regards to strict mode. In strict mode trying to modify a frozen object throws an error, while outside of strict mode it will fail silently.
@jmorrell this would be a significant departure from the way javascript preforms equality checks. The behavior should be the same for mutable and immutable reference types. ie.
let a = [1, 2]
let b = [1, 2]
a == a // true
b == b // true
a == b // false
let c = #[1, 2]
let d = #[1, 2]
c == c // true
d == d // true
c == d // false
the only way to have the behavior you want is to use singletons under the hood. ie. when declaring d
above, d
would have a pointer to the same location in memory as c
, so c == d
would be true
. this is similar to the way scala treats reference types, so that they behave like value types.