8 ms·
There is something about this immutability thing I really don't like. In the authors example it looks like v1[0] is set to 42 (const auto v2 = v1.set(0, 42)), a
by siempreb 7y ago
There is something about this immutability thing I really don't like. In the authors example it looks like v1[0] is set to 42 (const auto v2 = v1.set(0, 42)), at least that's how you read it because it contains the word 'set', but you have to 'know' that v1 is immutable and is actually not setting the value, but returning it. So IMAO the naming should be better, I like to read code that is not confusing.
If this construct, that I actually never needed in my entire career, is handy for undo/redo, why not just have a dead simple undo stack? In what situation is an immutable structure your best choice?
- agumonkey 7y agowhat would be a good word for this ? recreate ? recompute ? with_new ?
- physicsyogi 7y agoI'd probably be happy with "update", which is similar to Clojure's API for its persistent vector and map. https://clojuredocs.org/clojure.core/update https://clojuredocs.org/clojure.core/update
- masklinn 7y agoupdate is a different operation, it takes an update function for the value. The clojure equivalent to set is assoc.
- masklinn 7y agoClojure as `(assoc col key val)` and `(update col key fn)` though I don't know that the naming scheme is significantly clearer.
- ScottBurson 7y agoIn general, I think verbs should be avoided, when possible, as names of functional operations. For instance, in Java, 'BigInteger.add' should have been 'plus'. Compare: x.add(y); x.plus(y); People have been known to write the first one expecting 'x' to be updated. With the second, I think it's clearer that it's a value that needs to be assigned to a variable. For the operation in question here, I suggest 'with'. This is what I have used in my own functional collections libraries; I think it originated in SETL.
- skohan 7y agoI am a fan of nouns(or gerunds) for pure functions, and verbs for mutating functions. I.e: x.add(y); // x now equals x + y x.adding(y); // returns x + y without mutating x
- physicsyogi 7y agoPersistent data structures are thread-safe and are useful for concurrency. That's partly why they are a core part of functional programming languages like Clojure and Haskell. Persistent data structures are also useful for version control.
- maemre 7y agoThey are pretty useful for certain kinds of program analysis (like abstract interpretation and symbolic execution) where you need to create slightly modified copies of the program state (e.g. when you need to split the state on a conditional, or merge two states). Immutable data structures are (space) efficient for this use case. Moreover, you can combine them with hash consing for more space efficiency (only if you are creating lots of objects that share most of their data) and fast equality checking where there is only one copy a value in heap so equality checking is just object identity checking and addresses work as hash values now so hashing large objects is faster too. The algorithms used in these analyses are usually described in terms of mathematical objects which are inherently immutable and using immutable objects makes correctness proofs easier. When writing a piece of code, I start with immutable data structures because of the ease of mind they bring to the table (you can pass them around and you know they will not be modified so it makes reasoning and debugging easier, this is less of a problem in C++ because it has const references and const methods). Then, if an algorithm needs a mutable data structure or if the immutable data structures are too slow (e.g. the code is in the hot path) then I switch to mutable data structures. I think it is just a different mentality when approaching programming. It works for me because (a) it is the norm in my field (program analysis, or programming languages in general), and (b) I use a programming language with an ecosystem around immutable data structures (Scala).
- siempreb 7y agoThank you all for taking the time to explain, very valuable for me at least.
- deleted 7y ago[deleted]
- dfgdghdf 7y agoThe simplest answer is that immutability allows you to share data around without worrying about it being modified. In the OOP world, you might have a "getter" for a property (e.g. a list of some kind) that you don't want readers to be able to modify. Perhaps "set" was a poor name here, since it usually means something else in a C++ context. I quite like "with".
- dfgdghdf 7y agoShould add that C++ has const, making some of this irrelevant. In the C++ case immutability is about making minor changes to a structure more efficient by reusing the previous version. This is only possible with immutability because you need to be sure that your "base" structure won't change underneath you!
- slavik81 7y agoCan't copy-on-write achieve the same thing? If you keep a reference count, you can know whether it's safe to mutate the underlying data, or whether you need to make a copy first.
- heavenlyblue 7y agoTechnically copy-on-write is an implementation detail. It may or may not be implemented this way.
- bjoli 7y agoIn scheme, mutating functions end with "!". (append lst lst2) creates a new list without modifying lst, whereas (append! ...) is allowed, but not necessarily required, to mutate the list.
- hokkos 7y agoI see no problem with the set method name, this is what is used in other lib in other languages. When you use an immutable lib you understand the meaning and that every operation create a new base object. This is extremely useful for undo/redo, it basically comes for free with it. You no longer have to deal with invisible mutation, listener, event, you just generate the ui on each change from the base to the leaf, and everything is always correct for free. The advantage over an undo stack is that there is a re-utilization of unchanged intermediate data structure (what is change is only the path from the base to the changed element), so you can add a simple ref check in the ui to not redraw of the underlying data has not changed. So the perf come for free.