The important thing to notice in their example is the assignment to 'a' when pushing items (`a = a->push(1);`), and the ability to reassign the new change to a new variable (`auto b = a->push(4);`). That last command keeps 'a' the same (hence "immutable" data structure), but creates a new variable 'b' to hold the new data.
This technique allows you to, for example, pass the same data structure to multiple threads without worrying that they will simultaneously change data (race conditions).
More information can be found here https://en.wikipedia.org/wiki/Persistent_data_structure (check out the linked list and tree diagrams in examples).
For this to work properly in C++, I think you'd need to const all types for it to be effective (immutable::Array<const MyStruct>).
I'd really like to see benchmarks comparing these structures implemented in C++ vs those in functional languages; I don't know if the compiler optimization algorithms produce similar code.
They also have serious disadvantages : they can't be memory managed in the traditional way (since they tend to reuse other instances' memory in complex ways), and thus require a GC (refcounting can work, but ...). They are VERY allocation intensive, and they are worse than most non-persistent data structures. Assuming an O(1) allocator they can match non-persistent data structures in O-ness (ie. when making an invalid assumption that is quite popular in academia. In practice memory allocation is O(1) for small values, then O(n^2) once you get close to the system's memory capacity (scanning for holes in a long list) but don't go over it, and then O(oh fuck it let's just reboot this bloody BOAT ANCHOR) when crossing that line).
Clojure is famous for having good persistent data structures. Rich Hickey went touring academia touting the benefits of immutable/persistent/functional data structures : https://www.youtube.com/watch?v=dGVqrGmwOAw&feature=youtu.be...
There's also a famous book: https://www.amazon.com/Purely-Functional-Structures-Chris-Ok...
Yeah but how's that different than a const?
The big advantage is creating copies. Copying a list or vector is O(n) in both time and memory, while this is O(1) in both.
An std::shared_ptr to a const list or vector is O(1) in both for copies. But almost any modifications will be O(n).
For this data structure, almost all operations are O(log(n)).
A summary of their algorithmic complexities:
Copy:
std::vector : O(n) time and memory
std::shared_ptr<const std::vector> : O(1) time and memory
immutable::Array : O(1) time and memory
Append:
std::vector : Amortized O(1)
std::shared_ptr<const std::vector> : O(n)
immutable::Array : O(log(n))
Access at index:
std::vector : O(1)
std::shared_ptr<const std::vector> : O(1)
immutable::Array : O(log(n))
Insert at index:
std::vector : O(n)
std::shared_ptr<const std::vector> : O(n)
immutable::Array : O(log(n))
Replace at index:
std::vector : O(1)
std::shared_ptr<const std::vector> : O(n)
immutable::Array : O(log(n))