However, the following extension won't compile, apparently because the compiler doesn't recognize that the associated types became related once constrained.
Type1 can be any type conforming to P1 and Type2 can be any type conforming to P. There's no subtyping relationship between Type1 and Type2. In fact, if Type1 and Type2 are classes, either Type1 could be superclass of Type2 or vice versa.
In your example with protocols, foo1 doesn't accept an argument of any type that conforms to P1; it only takes an argument of type Type1. Likewise with foo2 as it relates to P and Type2. There is no subtyping relationship between Type1 and Type2, so there's no covariance to speak of. Associated types aren't just another way of spelling generics or existentials.
To give a very simple example: String and Int both conform to Equatable, and Int conforms to Comparable. Let's look at your example:
protocol Foo {
associatedtype Type1: Comparable
associatedtype Type2: Equatable
func foo1(_ arg: Type1)
func foo2(_ arg: Type2)
}
struct S {
typealias Type1 = Int
typealias Type2 = String
}
extension S: Foo {
func foo1(_ arg: Type1) {
self.foo2(arg)
// Of course I can't do this for S.
// So why would I be able to do this in an extension for Foo?
}
func foo2(_ arg: Type2) { }
}
Right. I got you, thanks.
It really doesn't make sense for concrete types. But it seems like it does make sense if I could pass only protocols or only concrete types as generic parameters or associated values. I was going to pass a protocol, which is the reason I didn't realize my mistake.