Protocol conformance for protocols with `var myVar: MyType { get }`

Recently I have bumped into a rather awkward issue with Swift of which I am sure other people have bumped into as well.

Why is it, that the code below generates an error Type 'ScaryKrakenCage' does not conform to protocol 'KrakenCage':

protocol Kraken {
    func roar()
}

protocol KrakenCage {
    var imprisonedKraken: Kraken { get }
}

final class ScaryKraken: Kraken {
    func roar() {
        print("roaaarghghghghghghg")
    }
}

final class ScaryKrakenCage: KrakenCage {
    
    let imprisonedKraken: ScaryKraken
    
    init() {
        imprisonedKraken = ScaryKraken()
    }
}

Whereas the code below is just fine (using a calculated property to return the underlying type, which in practice would exhibit the exact same behaviour):

protocol Kraken {
    func roar()
}

protocol KrakenCage {
    var imprisonedKraken: Kraken { get }
}

final class ScaryKraken: Kraken {
    func roar() {
        print("roaaarghghghghghghg")
    }
}

final class ScaryKrakenCage: KrakenCage {
    
    var imprisonedKraken: Kraken {
        return myImprisonedKraken
    }
    private var myImprisonedKraken: ScaryKraken
    
    init() {
        myImprisonedKraken = ScaryKraken()
    }
}

Can a compiler rule perhaps be added where if a protocol contains var myVar: MyType { get }, the first code snippet is allowed to compile. The compiler should be able to infer that ScaryKraken conforms to the Kraken protocol, and thus when we have let imprisonedKraken: ScaryKraken the compiler should be able to infer that ScaryKrakenCage conforms to KrakenCage.

In the case that we are dealing with a { get set } property, I would understand that the compiler complains. The protocol requires that all types of krakens can be set, thus only allowing ScaryKraken to be set would mean that the protocol isn't conformed to. But in the case above we are dealing with a get-only property...

If you want a concrete type that conforms to Kraken protocol you will have to use KrakenCage as a PAT (protocol with associated type).

protocol Kraken {
  func roar()
}

protocol KrakenCage {
  associatedtype ConcreteKraken: Kraken 
  var imprisonedKraken: ConcreteKraken { get }
}

final class ScaryKraken: Kraken {
  func roar() {
    print("roaaarghghghghghghg")
  }
}

final class ScaryKrakenCage: KrakenCage {
  let imprisonedKraken: ScaryKraken
    
  init() {
    imprisonedKraken = ScaryKraken()
  }
}

That should do the trick here. However you will loose the ability to use KrakenCage as an existential (protocol as a type). For example let array: [KrakenCage] will no longer be allowed by the compiler due to the associated type we just added to the protocol.

Thank you for your response!

But why is it in such cases necessary to go through the trouble of creating an associatedtype if the compiler should be able to fully infer the first code snippet I provided? To me it seems like some nice syntax sugar could be added here.

No it shouldn't, the compiler is basically only allowing you to write code exactly how you told it to with your protocol design. In the first original example you explicitly said that you want the conforming type to have a get-only property with a Kraken existential (a box type created by the compiler to store any type that also conforms to the Kraken protocol). Therefore the compiler awaits that you use Kraken and not a concrete type.
On the other hand if you use an associated type, the compiler awaits that you tell it which concrete type you want to associated with the conformance of the protocol.


OPTIONAL:

If you're curious to learn more, there is a huge discussion on how we eventually want to improve some of the generics UI in swift which also covers some of the 'existential' and 'opaque result type' landscape:

In short we have:

  • Kraken as a protocol
  • Kraken as a protocol type - existential, which eventually might become any Kraken
  • there is also potentially a some Kraken which is the opaque result type (also called reversed generics by others) that allows you to hide the concrete type for your user, but not from the compiler so that you can use that type in generic context.

There is a lot more to cover but I don't want overwhelm you with all that information. I do recommend you to read the official Swift book if you haven't already, it definitely covers the most essential part of these things.

1 Like

PS.: Don't be afraid to ask such question in #swift-users. There are a lot of community members that are happy to help. :slight_smile:

Thanks for the response!

I see why they do it like this, although I still don't necessarily agree. In programming languages such as Java, the first code snipped would be acceptable (provided that the value would be returned by a function as you can't define properties in Java interfaces). That's the reason I came to this question as I used similar constructs quite often in Java.

Anyhow, thanks for the discussion points, I appreciate it :)