associatedtype Protocol Generic

associatedtype on Protocol, making Protocol Generic



When we talk about generic code we have to come to an associatedtype for protocol. The protocol is the contract between two entities. And to make that contract more generic Swift provides the associatedtype for generic type on the protocol. To develop the proper Protocol Oriented Programming, POP, skill the associatedtype plays an important role. In this blog post, we will have a base understanding of the associatedtype on Protocol which will lead us to some more advanced level talk on the future blog post of this associated type series.

We have some talks over Protocol on our Swift Protocol Hub page. Where we evaluate the protocol usage on Swift. And on the last blog post, we talk about Swift Generic & Protocol where we talk about both Protocol and Generic. For any kind of confusion and deeper knowledge, we should visit those blog posts.

Background

associatedtype

associatedtype is an associated type on a Protocol. Associated means connected to. So when a type needs to be connected with a Generic protocol we use the associatedtype. Basically associatedtype is a placeholder type for a protocol. This placeholder type will be replaced by the concrete type at the time of implementing the protocol. Let us go through an example.


protocol Energy{
    associatedtype EnergyBar
    
    func consume(unit: EnergyBar)
    func loss()
}

On the above the EnergyBar is the associatedtype. This EnergyBar is used on the consume method as a parameter. As we want to make our Energy protocol as a generic one we have to make the type generic. That’s why we are using the associatedtype on this protocol. Now let us have an example of replacing that associatedtype or EnergyBar with a concrete type.

To have the concrete implementation of associatedtype we will take two entirely different characters. Popeye and Superman. We already know it is the Spinach that boosts Popeye with energy. So Spinach is the EnergyBar for Popeye. On the other hand, Superman doesn’t get that much of a bug out on the case of losing energy, but when he does he has to make sure there is a radiation source like Sun around him. So the SunRay is the EnergyBar for SuperMan.

Let us start the implementation. So the EnergyConsumption will have two ways; eating and radiation.


enum EnergyConsumption{
    case eat
    case radiation
}

Let define Spinach.


struct Spinach{
    let consumptionType = EnergyConsumption.eat
    
    let iron = 2.71
    let magnesium = 79
    let calcium = 99
}

extension Spinach: CustomStringConvertible{
    var description: String{
        return "\(iron) mg, \(magnesium) mg, \(calcium) mg"
    }
}

And we have a description of Spinach by confirming the CustomStringConvertible.

Now let us define SunRay. SunRay will have a different wavelength of light/radiation.


struct Wavelengths{
    let name: String
    let start: Float
    let end: Float
}

extension Wavelengths: CustomStringConvertible{
    var description: String{
        return "\(name): \(start)-\(end)"
    }
}

struct SunRay{
    let consumptionType = EnergyConsumption.radiation
    
    let visibleWavelengths = Wavelengths(name: "visibleLight", start: 0.4, end: 0.8)
    let ultravioletWavelengths = Wavelengths(name: "ultravioletLight", start: 0.2, end: 0.3)
    let infraredWavelengths = Wavelengths(name: "infraredRadiation", start: 0.9, end: 1.0)
}

extension SunRay: CustomStringConvertible{
    var description: String{
        return "wavelengths[micrometre]\n\(visibleWavelengths.description)\n\(ultravioletWavelengths.description)\n\(infraredWavelengths.description)"
    }
}

Again we are conforming the CustomStringConvertible to have a description.

Why & how confirming a protocol with associatedType turns into typealias?

Now it is time to conforming our Energy protocol on the Popeye and SuperMan struct.


struct Popeye: Energy{
    typealias EnergyBar = Spinach

    func consume(unit: Spinach) {
        "Energy is increased by \(unit.description)"
    }
    
    func loss() {
        "Energy lost"
    }
}

But why is typealias EnergyBar = Spinach rather than associatedtype EnergyBar = Spinach? What is the play between typealias & associatedtype? Let us talk about this in short.

So at the time of defining the generic protocol Energy, we have to inform the compiler about the type of unit parameter on the func consume(unit) method. Otherwise, the compiler will give us a compile-time error. Now we can not fix the type of that parameter to a concrete type yet. As we do not know what will be the type of that parameter unless the protocol is confirmed. So we say that EnergyBar is an associate type for the protocol Energy.

But things get a different perspective when we want to confirm the protocol. Now we need to say to the compiler that this time we are using the Spinach in place for Energy. So the Spinach will be the Energy. In other words, we are renaming the Energy to Spinach. And that action of renaming the type is done by typealias on Swift.

Protocol is the boundary/fence of a garden in the real world where the gardener will operate. The protocol sets the boundary of the operating area. And the associatedtype is the tools he will use at the time of gardening. One day the gardener can have a shovel. Another day he can have a fork. Whatever he uses, the shovel or the fork, it is another name of the tools.

Back to our original example. Swift is a modern language. It can infer the type. We can exclude the typealias keyword once we have enough context for the type. Now the following example we are excluding the typealias EnergyBar = SunRay.


struct SuperMan: Energy{
    func consume(unit: SunRay) {
        unit.description
    }
    
    func loss() {
        "SuperMan in now a human"
    }
}

Why associatedtype rather than generic type

This is an interesting question. Why we will use the associatedtype when we can use the generic type for a method even on a Protocol. Or more importantly when to use generic and when to use associatedtype on the protocol. To begin the discussion let us have an example of a couple of generic type-based methods on a protocol.


protocol EnergyOperator{
    func boostEnergy< T >(unit: T)
    func decreaseEnergy< T >(unit: T)
}

Here we are defining the EnergyOperator protocol, which has two generic methods under it. The boostEnergy and the decreaseEnergy both take generic type, T, as their param. The protocol confirming entity needs to define the solid types for those generic params. Let Popeye confirms the EnergyOperator.


extension Popeye: EnergyOperator{
    func boostEnergy(unit: SpinachUnit) {
        "Energy is now boosted by 1 Can full of Spinach it has \(unit) on per-unit"
    }
    
    func decreaseEnergy(unit: EnergyLoosingUnit) {
        "Popeye's energy is decreased by \(unit)"
    }
}

popeye.boostEnergy(unit: Spinach())
popeye.decreaseEnergy(unit: "Fighting")

popeye.boostEnergy(unit: Spinach())

Now here is the interesting part. For the boostEnergy and the decreaseEnergy methods they required two different data types. boostEnergy is using the Spinach datatype and decreaseEnergy is using the String datatype. But if we would have used associatedtype then they both should be having the same data type.

When to choose the generic type and when to choose the associated type on Protocol

Here is the most important question of this blog post. When we will choose the generic type and when to choose the associated type once we decide our protocol should be generic. Well, the very basic rule applies here. According to the need obviously. What the requirements need. We just need to notice the following to make the right decision for a generic protocol.

  • We need to use associatedtype when all the methods under a generic protocol need to use the same datatype.
  • The generic will be used when the methods under a protocol are required different data type for them individually.

Simple, right? Hmm. Now let us move to another discussion. What if we need to use the where clause on Protocol and even when we need to use those types of clauses? Also, what does the Self with capital S means when it comes to Protocol. Well, they need another blog post. This one is already a big one. So see you in the next blog post.

End talk

The definition of associate stands for a connection. Which draws the same meaning here in Swift also. We saw the association of a value with the case on enum which is called associated-values. And here on this blog post, we see the association of type with a protocol called associatedtype. Understanding the core meaning helps a lot to clarify the concept behind it. Hope we all got the message. See you in the next blog post. Till then take care.

Reference

Leave a Reply

Notifications for mobidevtalk! Cool ;) :( not cool