General +1, with some concern about complexity.
My compliments on this particularly well-written proposal. In my naiveté about the issue, I had many, many questions — and the proposal answered just about all of them all thoroughly and clearly.
My only reservation is about further increasing the cognitive surface area of the language. The rules surrounding opaque types are certainly going to be confusing for users — particularly the fact that
some P implies a different anonymous opaque type every time it appears. That is not going to always “just work” in a pleasant way. Careful attention to error messages will be crucial here.
I would be happier if there were a way to avoid this construct altogether. Given that people far more knowledgable than me are convinced it is necessary, however, I’m willing to defer to their judgement.
(Aside: why do Java / C# not need this? Is it because JIT compilation can achieve the same performance benefits by transforming interface method invocations to static dispatches, whereas Swift is always forced to go through a witness table when an existential is involved? Or is it something subtler?)
FWIW, I rather like the
some P syntax, at least in spirit. Reading the code samples in the proposal, I find it helps one reason heuristically about these curious beasts.
As an alternative, I’m tempted to argue in favor of ditching in-situ ad hoc opaque types altogether and only supporting opaque types via typealiases, as in the Future Directions section. The explicit naming of each opaque type could help clear up the confusion surrounding “
some P ≠
some P sometimes but not always.” It would give users an explicit type name to use when passing opaque types around. It would also help API authors understand exactly what types they are exposing. However, I can see that the proliferation of named types would be a nuisance.
First, what does one do if it becomes necessary to add an explicit type annotation for an opaque type? In other words, suppose I have this code:
var things = values.lazy.map(f).compactMap(g)
I assume this doesn’t work, because I need to specify that
things is the particular opaque type returned by
compactMap … right?
var things: some Collection<.Element == G> = values.lazy.map(f).compactMap(g)
So what is the explicit type of
things? Can I not specify one? What if I want to write a helper function that operates on
things, and doesn’t want the performance overhead of using an existential?
Second question, which I suspect reflects my ignorance about how ABI stability works. The proposal states:
opaque result types are only opaque to the static type system. They don't exist at runtime.
Opaque result types are part of the result type of a function/type of a variable/element type of a subscript. The requirements that describe the opaque result type cannot change without breaking the API/ABI. However, the underlying concrete type can change from one version to the next without breaking ABI, because that type is not known to clients of the API.
How does this happen? Does Swift’s dynamic library loading process stitch up client code to adapt to the now-possibly-different shape of the opaque type? Or are usages of opaque types dispatched through some kind of witness table?