4 ms·
Their Array class is perhaps unfortunately named, as it behaves as a vector or list (i.e. you can push things to the collection) rather than fixed size memory b
by akubera 10y ago
Their Array class is perhaps unfortunately named, as it behaves as a vector or list (i.e. you can push things to the collection) rather than fixed size memory block.
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 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.
- mmierz 10y agoNaive question: even so, what's the advantage over using, say, a const std::array<MyStruct>?
- iofj 10y agoWell, when you pass a variable around, it doesn't and cannot change. This means that different threads can't get in eachothers' way anymore, for instance, but also that you can't make a big chunk of mistakes at all. 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&t=1265 https://www.youtube.com/watch?v=dGVqrGmwOAw&feature=youtu.be... There's also a famous book: https://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504 https://www.amazon.com/Purely-Functional-Structures-Chris-Ok...
- GFK_of_xmaspast 10y ago> Well, when you pass a variable around, it doesn't and cannot change Yeah but how's that different than a const?
- iofj 10y agoIt's not, but it still has update methods. It's a const with update methods. Example for a map: x = map{"five": 5} y = x.put("six": 6} Now x is map{"five": 5} and y is map{"five": 5, "six": 6}. If any other tread was using x, it hasn't changed.
- kr7 10y agostd::array is not really comparable since its size is fixed at compile time. std::vector or std::list are more similar. 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))