Introduce Any<P> as a better way of writing existentials

The motivation of this proposal is that I find myself frequently having to explain the difference between protocols and existentials, as well as how they interact with generics. I see it frequently in my conversations with local developers, and it also comes up here on the forums fairly often. It's difficult to even talk about the difference between a protocol and an existential when they have the exact same spelling, so it's not surprising that users don't understand it.

I'm not proposing adding existential extensions right now. Joe Groff's "Improving the UI of generics" post from last year anticipates the ability to extend the existential type itself, using the spelling extension any P. I find myself wishing that we had the any P notation now (even before we have the ability to extend existentials), simply for the increased clarity it provides. It takes a confusing part of the language and makes it less confusing. I believe that has merits on its own.

However, for reasons I've tried to enumerate in the original post, I think the any P spelling is confusing. My main objection is that the natural reading of the following suggests that it extends any conforming type:

extension any Animal {
	func speak() -> String { ... }
}

If that is truly an extension of any Animal as is the obvious reading, then it seems natural that you should be able to call speak() on any animal. And yet, I cannot call dog.speak() or cat.speak(); I can only call anyAnimal.speak(). So the anticipated syntax extension any Animal seems actively harmful to me; if people are confused about existentials now, I worry that using that proposed syntax will make them only more confused in the future. extension Any<P> suggests that it clearly extends only a single type, not all types.

I'm also concerned that it's easy to get lost in the soup with a declaration like this:

// This is difficult to follow (for me)
var x: any P & Q where P.X == Q.Z = SomeStruct()

// This is less confusing to me
var x: Any<P & Q where P.X == Q.Z> = SomeStruct()

The <> delimiters help my eye recognize where the type declaration ends and the initializer expression begins. Again, though, we don't have the ability to write such expressions at all right now, so this is not an immediate concern. I just worry that if we go down the any P road, it will lead to confusion.

I've used the explanation that existentials are basically an Any<P> multiple times when answering questions from fellow developers, and have found it to be consistently useful in helping people understand what's happening. My hope is that we can get the clarity of a better spelling now, and I've tried to design it in a way that makes it compatible with future features.

So in short, my motivation is to address a source of active confusion: developers not understanding that var v: P introduces a new existential type. I also hope that I can nudge the syntax toward what I feel is a better spelling (Any<P> instead of any P), but if the consensus is that any P is the better spelling, I can accept that. Whatever spelling we choose, I hope we can get the benefit of a clearer spelling without having to wait for additional features.

Because of the need for source compatibility, I don't think we can force everyone to convert var v: P to var v: any P anytime in the near future. There's so much existing code out there using existentials that I'm not sure we'd ever be able to do it. I know I wouldn't want to go through and convert all the code in my own code base. But I'd still like the benefit of clarity for new code.

10 Likes