Swift: What are Protocols with Associated Types?
original source : https://www.natashatherobot.com/swift-what-are-protocols-with-associated-types/
I recently gave a talk about Protocols with Associated Types (PATs) – I was worried that my audience would already know everything about PATs, but turns out the opposite is true.
Many didn’t know what PATs were – which I should have anticipated in hindsight, since it took me a while to learn about them myself. So I wanted to explain them right here, especially since they are challenging to understand and there aren’t that many great explanations that I’ve found.
The best explanation that really helped me was by Gwendolyn Weston at the try! Swift conference in Tokyo (video here), so the example is inspired by her talk. There will be Pokemon…
Before PATs
I’m currently Level 9 in Pokemon Go, and I learned (thanks to my personal trainer @ayanonagon) that all Pokemon have some common traits, such as the power to attack.
Coming from Objective-C or other object-oriented languages, it might be tempting to have a Pokemon subclass for all the common functionality. Since each Pokemon attacks with a different power – lightning or water or fire, etc – we can use the power of Generics in our subclass:
// we have to ensure that the generic Power
// has an init function
protocol Initializable {
init()
}// Pokemon subclass
// Each Pokemon has a different Power,
// so Power is a generic
class Pokemon<Power: Initializable> {func attack() -> Power {
return Power()
}
}
At this point, we would have different Powers modeled:
// power types
struct ?: Initializable { // implementation }
struct ?: Initializable { // implementation }
struct ?: Initializable { // implementation }
Now, other Pokemon can subclass from our base Pokemon class, and they will automatically have the attack
function!
class Pikachu: Pokemon<?> {}
class Vaporeon: Pokemon<?> {}let pikachu = Pikachu()
pikachu.attack() // ?let vaporeon = Vaporeon()
vaporeon.attack() // ?
The problem here is that we’re subclassing. If you’ve watched Dave Abrahams’ WWDC talk on Protocol-Oriented Programming in Swift, you should be seeing the face of Crusty in your head right now…
The problem with subclassing is that while it starts out with great intentions, eventually things get a lot messier as exceptions arise (e.g. Pokemon Eggs can’t attack). I highly recommend reading Matthijs Hollemans’s Mixins and Traits in Swift 2.0 to understand this more.
After all, as Dave Abrahams stated, Swift is a Protocol-Oriented Language, so we need to change our Object-Oriented mindset…
Hello, PATs
Instead of subclassing, let’s do the same thing with PATs! Instead of subclassing all the things, we can create a Protocol that focuses on the Pokemon’s ability to attack. Remember, since each Pokemon has a different Power, we need to make that a Generic:
protocol PowerTrait {
// That’s it! An Associated Type is just
// different syntax for Generics in protocols
associatedtype Power: Initializablefunc attack() -> Power
}extension PowerTrait {
// with protocol extensions,
// we can now have a default attack function
func attack() -> Power {
return Power()
}
}
Now, any Pokemon that conforms to the PowerTrait
protocols will have the attack functionality no subclassing necessary!
struct Pikachu: PowerTrait {
// since we’re using the default attack functionality
// we have to specify the type of the associated type
// just like we did when subclassing with a generic
// * note that this is still called typealias, but will be changed
// to associatedtype in future versions of Swift
associatedtype Power = ?
}
let pikachu = Pikachu()
pikachu.attack() //?struct Vaporeon: PowerTrait {
// when the attack function is overwritten
// ? is inferred as the associated type
// based on the method signature
func attack() -> ? {
// custom attack logic
return ?()
}
}
let vaporeon = Vaporeon()
vaporeon.attack() //?
Conclusion
That’s it! A protocol with associated type is just a fancy term to a protocol that has generics in it. By using PATs (and protocols in general), we have a powerful tool that promotes really nice composition in favor of messy inheritance.
To learn more about the limitations of PATs and go deeper, I highly recommend Alexis Gallagher’s talk here.
Happy catching!