-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Immutable all the way #3
Comments
I think this is very important for this proposal because - like you said - making it immutable all the way enables transfer/sharing between threads, and that's a huge win. But also because allowing mutable data inside immutable data seems like a pretty big foot gun. With immutable all the way, you can't put functions inside immutable structures (since functions are objects that are mutable). But it would be really cool if there were immutable functions as well, which when executed returns a promise for when it was really executed. That way, you could share it with another thread, and when the other thread calls it, the real function is added to the event loop of the original thread. Something like:
It would be necessary to make sure that arguments passed to an immutable function could only be immutable data, and that they could only return immutable data. Or that any mutable data passed/returned would be automatically copied to an immutable object. If it's not immutable all the way, it raises some questions about value equality. If two immutable maps contain an object each, would comparing the immutable maps compare the objects inside them by reference equality or value equality? If by value equality, how would that work? With immutable data all the way, you can be sure that value equality can be somewhat cheap since you can compare a hash of the data (the hash still has to be computed, of course), but if there's mutable data inside it you have to make a deep equality check. |
Associating mutable data with points in an immutable structure can be done with Symbols and WeakMaps. (I think Symbols are already immutable) let asa = { name: "Asa", favoriteColor: "blue" }
const weak = new WeakMap();
const users = #{
asa: Symbol('asa')
}
weak.set(users.asa, asa) It might be slightly awkward that you have to pass the two around together let asa = weak.get(users.asa)
asa.favoriteColor = "green"
weak.get(users.asa).favoriteColor // 'green' |
If immutable structures can hold mutable data then it seems like they would require deep comparison to really know if they are equal or not. function memoize(fn) {
const memo = new WeakMap()
return function(object) {
if (memo.has(object)) {
// nothing has changed
return memo.get(object)
}
const ret = fn(object);
memo.set(object, ret);
return ret;
}
}
const doubleFooValue = memoize(function(object) {
return object.foo.value * 2;
});
const immutable = #{ foo: { value: 2} }
doubleFooValue(immutable) // 4
immutable.foo.value = 11
doubleFooValue(immutable) // wrong result: 4 edit: if you remove the |
@AsaAyers Symbols themselves are immutable in that you cannot assign new properties on them like:
But all symbols share the same prototype, and that's not an immutable object. So you can still do:
Not sure if that's an issue for transferability/shareability though, since the global prototypes on different threads would be different in any case, and sending a Symbol over threads would hopefully give that Symbol that threads global Symbol prototype. But I can imagine that this has some issues. |
2ality has some great info on Symbols and it mentions passing Symbols across realms (I think realms === threads for this purpose). I also decided to try some experiments in the console with that page open. The things I found unexpected were that while
|
I don't see any note in the document whether those immutable data structures can contain mutable items in them or not. I would personally advocate against allowing that since:
The text was updated successfully, but these errors were encountered: