Generalised concrete-type existentials


(Karl) #1

Now that SE-0156 has been accepted, I’ve been thinking recently that it might be nice to extend this proposal with some kind of generalised “concrete-type existential”. As I see it, there are two parts to an existential like the one proposed here:

<concrete type> & <protocol conformances...>

For classes, the “concrete type” part of this relationship is flexible: the instance may be the exact class X, or some subclass of X, and in that case it may or may not conform to additional protocols. So that’s why this existential has value; because, say, “NSObject & Collection” may be satisfied for some subclasses of NSObject.

Value-types cannot have subtypes, so that part of the constraint is fixed. Likewise, until now, the set of protocol conformances for a concrete type was fixed. That is to say that “Array <T> & Equatable” would be a nonsense constraint, because Array<T> does not conform to Equatable and will never conform to Equatable.

But we have approached everything focussed on the idea that only flexibility in satisfying the concrete-type constraint matters. Now that we have conditional conformances, however, the protocol-conformance side of the equation also becomes interesting, even for value-types. The existential “Array<T> & Equatable” is no longer a nonsense constraint - it is a valid constraint which implies some underlying constraints on T. In this case the underlying constraints might be simple (T: Equatable), but in general for any protocol “SomeProtocol”, the conditional conformance requirements might be long and complex, or even completely unknown to us (e.g. if the Foo: SomeProtocol conformance is added retroactively by another module).

For example, I should be able to write:

func checkArray<T>(_ a: Array<T> & SomeProtocol) -> Bool {
// …
}

Without needing to be copy-paste the requirements which make Array conform to SomeProtocol, or even needing to know the specifics of those constraints at all.

The solution would be to extend SE-0156 to allow { <non-class concrete type> & <protocol conformances> } existentials.

- Karl


(Karl) #2

That is to say that “Array <T> & Equatable” would be a nonsense constraint, because Array<T> does not conform to Equatable and will never conform to Equatable.

Actually, now that I write it out, even that isn’t true. Somebody might retroactively add conformances after importing your library. In general, no public type knows the complete set of protocols it conforms to when it is compiled; it doesn’t matter if it’s a class or a value-type.

So there is some value in a generalised concrete-type existential even without worrying about conditional conformances.

- Karl