The details are more complicated. Swift supports separate compilation in addition to values conforming to protocols. This means that you must have some universal representation to operate on, as a public function cannot see all callers or their types. Of course, for internal or intra-module calls/types, the compiler aggressively specializes and de-virtualizes when possible (accounting for code-size tradeoffs).
Swift's existential containers have inline buffers to store small values directly, say 2 or 3 pointers in size. Also, there's been continual, active work (CC @Arnold) to stack allocate more and more language constructs.
@sbohmann, as someone who has implemented persistent collections in the past I am unsure which functionality would not be supportable via the existing classes. Would the goal for these implementations be to offer non-functional (performance/memory) benefits?
To be more specific in my question - should Dictionary hypothetically be replaced in Swift 5 with a HAMT implementation (with allowances for a more traditional hashing strategy), would this result in its public API changing? My suspicion was that this would be transparent (barring side effects like iteration order)
The existing Array, Dictionary, and Set are not implemented using typical persistent collection algorithms today, but have immutability via CoW.
It is kinda terrible, but it is a limitation until we have generalized existentials.
So you know, type erasure is not the same in Swift as in Java. Type erasure is done by capturing the use of the concrete type within a function. So for example, AnySequence works by proxying the makeIterator() call - and to save space has implementations of all the other Sequence methods based on that call.
The difference is in the case of shared large collections. Copy on write did in my tests become very costly in this case. For small collections, I of course expect the existing built in array and dictionary to be way faster, given their relative algorithmic simplicity and massive optimization I would therefore and generally not propose replacing anything.
The use case is not only concurrency but every case with large statelessly shared collections. It doesn't take concurrency or coroutines to make them necessary; stateless sharing is simply a way to go about things that has become quite popular because of the way it reduces overall complexity.
Swift's classic collections with their COW support directly play into avoiding shared state. It's just that the performance can really degrade for large collections, which is why all most programming languages supporting functional programming now have them