Wednesday, June 1, 2016

Swift Type Erasure

Rob Napier:

Once upon a time, when Swift was young, there were a couple of types called SequenceOf and GeneratorOf, 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 SequenceOf became AnySequence and GeneratorOf became AnyGenerator and there are a gaggle of indexes and collections from AnyForwardIndex to AnyRandomAccessCollection.

[…]

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 a Zip2Sequence<...>? 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 Any and AnyObject, which are protocols that just “hide” the type. You can still as! an AnyObject back to its original type. AnySequence and 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 making as! 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.

Samuel E. Giddins:

To start out, we’re going to write a small class that also conforms to SequenceType and is also generic over the Element we want […]

“But wait!” you probably want to say, “that doesn’t really conform to SequenceType!” It does. This is using the same fatalError trick I mentioned earlier, to gloss over the holes in the SequenceType type signatures we can’t possibly know how to fill.

[…]

The most important thing to notice about this class declaration is that _AnySequenceBox is generic over a particular SequenceType implementation. In fact, it’s generic over the very type that our AnySequence wrapper is erasing. It’s also a subclass of _AnySequenceBoxBase, with the base’s generic element being that of the erased type’s Generator.Element. This is the point of translation from our associated type protocol into the world of generics. The rest of the implementation for AnySequence is basically just boilerplate (albeit a vast quantity thereof).

[…]

Yes, we’ve implemented the methods of SequenceType three 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.

Gwendolyn Weston:

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.

Russ Bishop:

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.

Natasha Murashev:

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.

Joe Groff:

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 […]

Leave a Comment