mstade / funkis

Functional JavaScript
MIT License
6 stars 0 forks source link

Is additive mutability dangerous? #14

Closed mstade closed 9 years ago

mstade commented 10 years ago

This library presumes that mutability is in most cases bad and should be avoided. Copy-on-write or sharing should be used, along with new instances that represent subsequent states. An example:

const foo = Object.freeze([1, 2, 3])
const bar = Object.freeze(foo.concat([5, 6, 7]))

In this example, both foo and bar are made immutable, and so the only way for bar to be defined is by virtue of copying foo into a new array. In this example, we're never mutating foo, but we are mutating the environment – twice. First, foo is declared, then of course, bar. However, this is additive, meaning foo and bar didn't exist at all before their declaration. As such, one couldn't really reason about them at that point either (other than possibly asserting their non-existance.)

So my thinking here is, is additive mutability on object – not just the environment – similarly "safe", as it were? If foo wasn't sealed, but properties could be added just not changed or removed once set, would that make foo more difficult to reason about?

In a way, yes, because it does mutate the internal state of foo. If you add a property like so:

const foo = [1, 2, 3]
foo[3] = 4

... it won't just affect the new property, but also change the internal state of foo such that it's length property updates its value. As well, if there's code to reflect on foo to enumerate all its properties it would unfortunately return even the new property. This does change the state of the world and as such is tricky to manage.

However, what about less obvious properties, such as "internal" properties? An example is adding metadata to an object. The safe and strictly immutable way of doing this is to create a new object, add the metadata as a property of some kind, and make sure to copy over all the fields of the old object (copy-on-write semantics) or make sure the new object has the old object as a prototype (structural sharing.) Everytime you change a property, the same thing has to happen.

In especially copy-on-write scenarios, but possibly also with structural sharing (via prototypes at least) this may have some non-trivial performance concerns. So my question is this: are there cases of additive mutability which are not dangerous, and could be exploited if necessary? Needs some investigation.

mstade commented 10 years ago

An example of where additive mutability is necessary is with WeakMap shims. The only way to make sure a map only keeps weak references is to store the values of the map on the keys themselves. This means the map creates a property on the key, and once the key is no longer referred to anywhere it – along with the reference to the value – goes away.

This kind of implementation is impossible with objects that are sealed for added properties. Presuming the property doesn't change any other state (such as indices on an array changing length) – is it still dangerous mutability? One could argue this case is special, a matter of sorta-kinda-private state. Possibly relevant still.

mstade commented 9 years ago

Pointless discussion, just add immutable datastructures and be done with it.