SE-0335: Introduce existential `any`

I did start a discussion back in October about the usability of generics as a follow-up to a generics vision document that was posted over two years ago. In my discussion thread, it was made very clear by many community members that the place we should start is clarifying existential types, and I took that feedback very seriously. @Jumhyn 's comment from that thread sums it up clearly:

This is true for function parameters, but it is not for stored instance properties. A stored let -constant with existential type cannot change dynamically for a single instance of the enclosing type, but there isn't much you can do with that assumption because the underlying type of the property can certainly change across instantiations of that type, which means the entire type must be parameterized on that underlying type of its stored property. This is a much bigger semantic difference than using an existential type, and I do not think introducing semantic differences between stored properties with existential type and all other kinds of storage is a good idea for the programming model.

The type system differences apply to functions, too. Here's a contrived example:

protocol P {}

func generic<T>(_ arg1: T, arg2: T) { ... }

func existential(arg1: any P, arg2: any P) {
  generic(arg1, arg2)
}

The above code is valid because arg1 and arg2 have the same static type. This is not true if you treat each any P as an independent type parameter, which is what the semantics would need to be in order to pass two different value types conforming to P at the call-site.

There are also ABI differences between type parameters and existentials, and we can't change the ABI of a function based on how its parameters are used in the body. For all of these reasons, I firmly believe treating existentials as type parameters where possible is best left as an optimization.

I think a much better approach is for programmers to be explicit with any, and in the future, we could consider "defaulting" a plain protocol name to mean some instead of any, and encourage developers to think of the plain protocol name as an implicit type parameter rather than an existential type, as outlined in the future directions.

I'm not totally happy with Any and AnyObject in this proposal either, but I don't know that any alone as a replacement for Any works. It doesn't allow you to distinguish between the existential and singleton metatype unless we keep .Protocol around. If we make today's Any only usable as an existential type, we would be removing a feature from the language -- a way to explicitly write an unconstrained requirement -- which is arguably not useful, but it does change the fact that this migration is entirely mechanical (even the corner cases around typealias are mechanical by splitting the typealias into two). Perhaps it's possible to evaluate the likelihood that somebody out there has written some Any in their code. And maybe it's fine if the change isn't entirely mechanical, but I think the source change is a lot more acceptable if a tool can do it for you.

I'm completely open to more strongly considering any Value and any Object. My primary hesitation is that Value isn't the best name for an empty protocol composition.

A future direction is not a commitment or POR, nor does it mean a potential feature is uncontroversial. I do think that existential opening -- something you've frequently advocated for in other discussion threads -- would greatly alleviate the need for this feature in the majority of use cases, but I believe there are still rare use cases where an extension on an existential type would be useful. In any case, it's just a future direction.

5 Likes