I hope this is the best place for this question.
Today I came across something in the design of the Swift language that baffled me, and I can't find a design explanation for why this works the way it does.
Let's suppose I have the following code:
protocol Pet { }
protocol Human {
var pet: Pet { get }
}
struct Cat: Pet { }
// This does not compile:
// type 'Italian' does not conform to protocol 'Human'
struct Italian: Human {
var pet: Cat
}
Why isn't this possible? Whatever a human with a pet can do, an Italian with a cat can do as well, and this is ensured by the fact that Italian conforms to Human and Cat conforms to Pet.
What am I missing here?
Thanks,
Lorenzo
P.S. I know already about alternative solutions, I'm just wondering why this language design decision has been taken.
Unfortunately, covariance isn't allowed in that context right now. You can do:
protocol Pet { }
protocol Human {
associatedtype P where P: Pet
var pet: P { get }
}
struct Cat: Pet { }
// Works
struct Italian: Human {
var pet: Cat
}
This is SR-522. I think the main reason it's not implemented is because it would be more work, not because anyone thinks it's not a good idea. However, changing that behavior now could break source compatibility, since there might be a default implementation of a requirement that wasn't being considered and now is.
I’m curios why covariance should be allowed here? The Human protocol requires a var that can store any Pet, but the Italian struct defines a var that can only store Cats. It seems to me the compiler is correct that the protocol is not satisfied. What if you had a function accepting a Human existential which tried to assign a Bird as the pet, and you passed an instance of Italian?
Edit: never mind, just noticed it’s {get} only. Sorry for the noise.
Even though covariance could be allowed, I'm not sure it's a good idea because of how it interacts with associated type inference. If an associated type is not specified, it can be inferred today by looking at the types of witnesses. With covariance, we'd have to compute joins, and rejecting certain witnesses because their types don't match an associated type that has already been inferred becomes more difficult.