Swift Type Erasure
Once upon a time, when Swift was young, there were a couple of types called
SequenceOfandGeneratorOf, and they could type erase stuff. “Type erase?” you may ask. “I thought we loved types.” We do. Don’t worry. Our types aren’t going anywhere. But sometimes we want them to be a little less…precise.In Swift 2, our little type erasers got a rename and some friends. Now they’re all named “Any”-something. So
SequenceOfbecameAnySequenceandGeneratorOfbecameAnyGeneratorand there are a gaggle of indexes and collections fromAnyForwardIndextoAnyRandomAccessCollection.[…]
Not only is the type overwhelming, but it ties us to this particular implementation. […] Clearly we’re leaking too much information about our implementation. What’s the point of
reverseZip? Is it to return aZip2Sequence<...>? No. It’s to return a sequence of tuples. We want a type that means “a sequence of tuples.”[…]
These “Any” type erasers also aren’t like
AnyandAnyObject, which are protocols that just “hide” the type. You can stillas!anAnyObjectback to its original type.AnySequenceand its kin completely encapsulate the underlying data. You can’t get the original back. This creates a very strong abstraction layer and strengthens type safety by makingas!casting impossible.[…]
This kind of type eraser lets us convert a protocol with associated types into a generic type. That means we can put it in properties and return values and other places that we can’t use protocols directly.
To start out, we’re going to write a small class that also conforms to
SequenceTypeand is also generic over theElementwe want […]“But wait!” you probably want to say, “that doesn’t really conform to
SequenceType!” It does. This is using the samefatalErrortrick I mentioned earlier, to gloss over the holes in theSequenceTypetype signatures we can’t possibly know how to fill.[…]
The most important thing to notice about this class declaration is that
_AnySequenceBoxis generic over a particularSequenceTypeimplementation. In fact, it’s generic over the very type that ourAnySequencewrapper is erasing. It’s also a subclass of_AnySequenceBoxBase, with the base’s generic element being that of the erased type’sGenerator.Element. This is the point of translation from our associated type protocol into the world of generics. The rest of the implementation forAnySequenceis basically just boilerplate (albeit a vast quantity thereof).[…]
Yes, we’ve implemented the methods of
SequenceTypethree times. And you’d need to do this again for every protocol you wish to provide a type-erased wrapper for. It’s rather a lot of code to write, but doing this work will make your protocols feel more like first class citizens.
This is where type reification comes in: We make an abstract type concrete by filling in its placeholder types.
[…]
Wrapper classes are conventionally prefixed with the word “Any”, in order to guarantee that you will instantiate an object that implements our protocol and fills the generic type, without necessarily having the implementation on hand.
[…]
I feel the best definition that I have at the end of my road of preparing this talk is that type erasure is this design pattern where you have a wrapper class that has a constraint in the initializer method such that you can only initialize it with an instance of an implementation of the protocol you are trying to make concrete.
More specifically, what is type erasure in the context of Swift and Protocols with associated types? If I could boil it down to one sentence:
Passing a generic type parameter to a protocol’s associated type member.
This still feels like an overly complex fix for something that should just work and hopefully it will in the future. For now, this is a super good pattern to know when working with protocols with associated types.
See also: Type erasure in C++: how boost::shared_ptr and boost::function work.
Previously: Swift Protocols With Associated Types.
Update (2016-06-01): David Owens II:
If you have to keep using type erasures to actually work with your types, doesn’t that suggest something? Maybe I don’t get it.
Being only as precise as you need to be lets you stay flexible and avoid unnecessary refactoring.
Only keep the types you need. Type hoarding is unhealthy and may require professional help.
Update (2016-06-02): Elmar Kretzer posted an example of a type-erasing struct.
Update (2018-03-09): See also: Hector Matos and Mike Ash (Hacker News).
1 Comment RSS · Twitter
[…] Swift Protocols With Associated Types, Swift Type Erasure, Patterns for Working With Associated […]