I have been looking for surprising side effects in the interaction of this pitch with the fact that overload resolution favors existentials.
First, a reminder of what I mean by "overload resolution favors existentials". It is this:
protocol P { }
struct S: P { }
func foo(_ x: any P) { print("P existential") }
func foo(_ x: some P) { print("P generic") }
// Prints "P existential"
foo(S())
Yes, that's how the language works today - one of its little gotchas. It's hard to consider the existential version as "more specific", and it would be interesting to know why avoiding the generic and specializable method was considered as the best implementation strategy. Anyway, that's not the point of this post. Overload resolution favors existentials, that's what's important.
So I found this. The code below changes its behavior depending on how the bare P
is interpreted:
protocol P { }
protocol Q { }
struct S: P, Q { }
func foo(_ x: P) { print("Bare P") }
func foo(_ x: some P & Q) { print("P & Q generic") }
// Prints "Bare P" if P is interpreted as `any P`
// Prints "P & Q generic" if P is interpreted as `some P`
foo(S())
I have difficulties deciding if this would create a lot of churn. But this definitely is a breaking change that is worth mentioning in this pitch.