I briefly mentioned this in the Introduce Any<P> as a better way of writing existentials - #51 by Nickolas_Pohilets, but initial edit was somewhat hard to read, and idea seems to be lost in the discussion. But I think this is still relatively unexplored area, and deserves more discussion.
In was thinking on several messages from @Karl, highlighting that existentials first of all are boxes for generic types. And from that perspective, current syntax for existentials is quite limited. We can express box for T where T: P
, and that's pretty much it for today. There are some discussions that would enable us to express boxes for T where T: Collection, T.Element == Int
(suggested syntax was Collection<.Element == Int>
) and Array<T>
(with syntax any Array
).
But what if we can generalise this even further, and have a syntax that allows to box any generic type?
What about a box for Dictionary<KeyWrapper<T>, ValueWrapper<T>> where T: P
? Here types of key and value are related. And responsibility of the existential type should be to preserve this relationship until existential is unboxed.
If we try to think of existential syntax that is expressive enough to preserve all the information from the underlying generic type, then we will end up with something like this:
any<T> Dictionary<KeyWrapper<T>, ValueWrapper<T>> where T: P
If consists of the tree parts:
-
any<T>
- lists generic types captured inside the existential, required -
Dictionary<KeyWrapper<T>, ValueWrapper<T>>
- defines the type of the value captured inside the existential, required. -
where T: P
- lists additional conformance and equality constraints, optional.
Writing it as any<T: P> Dictionary<KeyWrapper<T>, ValueWrapper<T>>
also should be possible.
Then we can think of the existing existential types as syntax sugar/type aliases for the full syntax:
typealias Any = any<T> T
typealias AnyHashable = any<T: Hashable> // = any<T> T where T: Hashable
typealias AnySequence<T> = any<U> U: Sequence where U.Element == T
protocol P {}
var x: P {} // syntax sugar for var x: any<T: P> T
When unboxing such type, I think we should be binding the types from the any<...>
part.
let boxed: any<T: P> Dictionary<KeyWrapper<T>, ValueWrapper<T>> = ...
// T is bound to the generic parameter of KeyWrapper/ValueWrapper, not entire dictionary.
// Conformance T: P is implied.
let <T> unboxed = boxed
for key: KeyWrapper<T> in unboxed.keys {
// ...
}