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?
They 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).
I 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.
Should 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!
Can'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.
Admittedly, it's pretty easy to accidentally make unnecessary non-const accesses in modern C++. An immutable API would certainly avoid a lot of qAsConst casts.
Persistent 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.
In 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.
In 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.
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?