On the other hand, in Rust, we have the compiler on our side. If one grabs a mutable reference to the state (&mut Model in the update function in https://github.com/DenisKolodin/yew/blob/master/examples/cou...), then the Rust compiler ensures that there are no other references to that Model. Thus, when the framework does `nextState = mutate(¤tState)`, one is guaranteed that there are no existing references to `¤tState`, and thus it is impossible for a shadow update to occur.
IMO, this is better not primarily for performance reasons, but because it is conceptually easier to mutate a state object instead of using reducers. (I haven't looked into this framework much, so I don't presume to speak about it's internal implementation, so this may be off in some framework-specific details.)
Maybe Haskell goes too far in this regard, but Rust is in line with the same way of thinking, because mutability is surfaced and checked at type level.
Debugging and rewind/replaying application state is also greatly simplified by keeping states immutable.
Having the state mutable would also need some kind of locking between the render view and updateState method
You may want to brush up on Rust, the language has been designed explicitly to deal with these kinds of situations.
That's right, but that's exactly the main strength of Rust : the borrow checker deals with that locking at compile time with zero runtime overhead.
Not arguing on the other points though.
This kind of performance optimization, especially outside of game engines seem unnecessary.
so you mean bloody expensive ? seriously, if everybody thinks like you no surprise the current web crawls on beastly computers.
The only way to address it is with extensive pooling and heuristics.
Or you can just mutate.
Really it's no wonder that the web is so slow if the common conception is that allocating is no big deal. If you really want to do performance right allocations should be at the top of your list right next to how cache friendly your data structures are.
When a GC allocates memory all it does is check if there is enough memory in the "young generation" if yes it will increment a pointer and then it will just return the last position in the young generation. If there is not enough memory in the young generation the GC will start traversing the GC root nodes on the stack and only the "live" memory has to be traversed and copied over to the old generation. In other words in the majority of cases allocation memory costs almost as much as mutating memory.
Mutable language GC's like JS or Java's aren't necessarily built for this compared to GHC. And even discounting all that, things like stack memory, cache, etc all make a huge difference in real world performance. GC's have come a long way, but there is still a gap in performance.
"Immutable" data structures are not just objects that never get mutated, they're different manners of organizing the bytes.
However, most JS runtime have a generational GC so an allocation isn't remotely as costly as an allocation in C or Rust.
[1] https://reactjs.org/tutorial/tutorial.html#why-immutability-...
When you use `Object.assign()` then you're doing it, but you don't have to use that, and probably shouldn't.
Afaik you don't have to. It just makes things easier since state changes can be detected through shallow reference comparisons. There is even the shouldComponentUpdate() hook to allow the user to detect state changes which did not trigger a whole state object change.
In theory there would be no difference between mutating an existing memory area and allocating new memory to write to it. The number of bytes written to RAM are the same.
It's not the same, you need to allocate new memory, if it's on the heap then (depending on your allocator/OS etc) you will be making a syscall, which means context switch, cache eviction etc.
That means a difference of ~300 cycles before you can even start working with the cache line you pull from main memory.
I think frameworks like React should explicitly demand that state and props will be referentially compared to optimize the rendering.
I have used a lot of redux in js and written a few things in Elm, and the immutible property of application state is very central in both, and an important part of being able to reason with the ui, as well as events happening concurrently.
The problem with shared mutable state might appear to be then “mutable” but it’s the combination of shared+mutable.
Ownership removes one (if it’s mutable it’s not shared). Immutability removes the other.
The outcome is the same: you can reason about your code, including concurrent code.
The price of ownership is some extra mental overhead. The price of immutability is some extra copying and cumbersome updates.
In JS/Elm you have no choice but to manage shared mutable state using pure functions and immutable data. In rust you can use either model.
Not really. Of course you can copy immutable data upon update, but persistent data structures (large shared state between data versions)[1] are hard to write without GC...
[1] https://en.m.wikipedia.org/wiki/Persistent_data_structure
[1] https://doc.rust-lang.org/std/rc/struct.Rc.html [2] https://doc.rust-lang.org/std/sync/struct.Arc.html [3] https://manishearth.github.io/blog/2016/08/18/gc-support-in-... [4] https://github.com/crossbeam-rs/crossbeam
[1]: https://github.com/asajeffrey/josephine
[2]: mozilla's JS VM
[3]: this is how you call a doubly linked list managed by the JS GC https://github.com/asajeffrey/josephine/blob/master/examples...
The problem is that the banana has a jungle attribute. If you pass a bannana to a function you're also implicitly passing in the entire jungle. The function could mutate the entire jungle without your knowledge. If you remove the jungle attribute from the bannana class you have to explicitly pass the jungle as a function parameter. This makes it far easier to understand the code.
The same applies to return values. If you return every changed value from a function and then explicitly handle the change at the callsite it doesn't actually matter if the data is mutable or immutable because the side effects are visible and easy to understand.