Protocol with protocol variable

Hi there,

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.

1 Like

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
}

to work around the above problem.

1 Like

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.

2 Likes

It is also not clear how covariance would work with associated type inference.

3 Likes

I ran into the same "issue" myself a few days ago. Thanks @suyashsrijan for the workaround!

1 Like

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.

1 Like