First, the tweets. Answer follows.
@ericasadun Swift Generics wreck my head. Took my 2 hrs yesterday to hit on "extension UnsafeMutablePointer where Memory: IsAudioUnit"
— charlesism.com (@charlesismcom) August 1, 2015
@ericasadun "IsAudioUnit" is my protocol, but where the heck does "Memory" come from? From hours of Googling! Just a magic word to me.
— charlesism.com (@charlesismcom) August 1, 2015
Generics use three kinds of tokens in addition to actual type names and two kinds of constraints to build type restrictions. Rules place limits on implementations to ensure that code applies only to types that can provide the functionality the generic implementation offers.
The three three token types are type parameters, associated types, and protocols. The two kinds of generic constraints are conformance constraints, which look like this:
where Generator.Element: Comparable
and same-type constraints, which look like this:
where Generator == IndexingGenerator<Self>
First, there are type parameters. Unlike function parameters that represent instance values, type parameters represent types. They can be used to declare variables or to indicate return types, etc.
Type parameters appear to the right of the construct name and appear within angle braces. Here’s the declaration for UnsafeMutablePointer in the standard library. It declares one type parameter, the Memory token you were asking about.
struct UnsafeMutablePointer<Memory> : RandomAccessIndexType, BidirectionalIndexType, ForwardIndexType, _Incrementable, Equatable, Strideable, Comparable, _Strideable, Hashable, NilLiteralConvertible, _PointerType { // ... stuff ... }
You find this module declaration and code-level documentation by command-clicking any UnsafeMutablePointer symbol in an Xcode editor. This action transports you to the standard library module interface, where you find up-to-date declarations and documentation that far outpaces any material you’ll find in official doc sets.
This is declaration the keyword Memory comes from. It’s a type parameter used with unsafe mutable pointer instances. You get a better sense of what this item means from the standard library header docs, which precede the declaration
A pointer to an object of type `Memory`. This type provides no automated memory management, and therefore the user must take care to allocate and free memory appropriately.
The documentation the goes on to describe the different states a pointer can encounter.
Back to generics. Each construct can declare zero (no angle brackets) or more type parameters (separate them with commas). For example, dictionary declares two type parameters:
struct Dictionary<Key : Hashable, Value> : CollectionType, Indexable, SequenceType, DictionaryLiteralConvertible
Those type parameters can also conform to protocols. A Dictionary key must be Hashable. This conformance appears within in the type parameter brackets.
An associated type is declared by a protocol using the typealias keyword. It normally set by an item that conforms to that protocol, although you can supply a default. Like type parameters, an associated type can be used as a token when building generic type rules.
Here’s the declaration of CollectionType:
protocol CollectionType : Indexable, SequenceType
And here’s a simple protocol extension that creates a computed property:
extension CollectionType where Generator.Element: Comparable { var maxIndex : Index? { return self.indices.maxElement({self[$1] > self[$0]})} }
So where does the phrase Generator.Element come from in this case? Construct types can also conform to protocols. These conformances appear after any angle brackets and a further colon. CollectionType conforms to SequenceType and SequenceType provides the Generator stand-in. Generator is a GeneratorType. GeneratorType then provides Element.
As you can see in the following summary, both Generator and Element are associated types for CollectionType. (Don’t go looking for this output just yet because I built it, not Xcode.)
protocol CollectionType Conformances: Indexable SequenceType Associated types: Distance : _SignedIntegerType = Int [ForwardIndexType] Element [GeneratorType] Generator : GeneratorType = IndexingGenerator<Self> Generator : GeneratorType [SequenceType] Index : ForwardIndexType [Indexable] SubSequence : Indexable, SequenceType = Slice<Self>
The third and final token type is a protocol. For the maxIndex example, Comparable ensures that elements can be evaluated against each other using the greater-than operator. This conformance is applied against collection elements in the protocol extension that implements the maxIndex property.
In terms of finding these terms, it’s tedious but not terribly hard to scan the standard library for protocols and construct declarations and work through the hierarchy to find a full list of a type’s conformances, associated types, and type parameters. Here’s the list for UnsafeMutablePointer:
struct UnsafeMutablePointer Type Parameters Memory Inherited Type Parameters: Distance : _SignedIntegerType = Int Stride : SignedNumberType Stride : SignedNumberType [_Strideable] Conformances: RandomAccessIndexType BidirectionalIndexType ForwardIndexType _Incrementable Equatable Strideable Comparable _Strideable Hashable NilLiteralConvertible _PointerType
It’s a lot easier to just let Xcode do the work for you. You’ll find the details about automatically compiling terms at this gist, which I pointed out in my post a few days ago. Automating the process enables you to gather related terms together and refresh those references as each new beta appears.
I’m working on the Swift Developer’s Cookbook for Pearson/Addison Wesley. This write-up isn’t exactly in the book but similar material is covered in Chapter 5 on Generics and Protocols. Send me your questions and thanks for buying my books!
One Comment
Thanks Erica!
I just want to point out the horribleness of my naming a protocol “IsAudioUnit” before any else can fault me for it 🙂 Yes, it is true that the “is” prefix makes it sound like it’s a Bool, and also true that since it’s a Swift Protocol, it ought to end with “-able” or “-ing.”
It was a pleasant surprise to find you devoted a post to this. I’m less surprised by the depth of your explanation, which is great as ever.