the typed throws feature is still in its very early stages and we have probably not gathered enough real world experience from a diverse enough group of users to be able to make recommendations about what patterns work and don’t work in practice.
it wasn’t too long ago when it was widely thought that macros were a C-ism that Swift was better off having discarded and that attempts to replicate code generation capabilities that other languages had were inherently misguided. but experience proved otherwise and thankfully people in high positions kept an open mind, and so today we have some code generation capability in the language that is becoming usable in an increasingly broad range of build systems.
We actually have almost thirty years of experience seeing people chase precise exception typing in Java in exactly the patterns folks are now hoping to duplicate in Swift.
I read through the thread@xwulinked to a couple days ago, and I’m still not sure why sum-types-lite (that is, anonymous discriminated unions) are still considered off the table.
We still have tuples even though structs are usually favored. In fact, thanks to variadic generics, @Slava_Pestov has proposed making tuples conform to a new Tuple protocol, increasing their utility. Tuples have proven to fill a specific niche, where structural typing and inline definition are desirable. This seems to be particularly the case for arithmetic methods that report overflow. Are we certain that there is not a similar niche for structurally typed, anonymous, inline-defined enumerations? For example, what about Regex capture groups?
I don't think there is a common part in your example, a protocol with an associatedtype needs to be identical for it to be considered a common part, and since it's not, getAssoc() shouldn't be accessed at this point.
Personally, I'd just disallow this. In the union type I'd only allow concrete types and forbid mixing derived and base classes to keep things unambiguous. The same limitations as using a protocol for the same purpose:
What's different from a protocol is the union type guaranties the list of "conforming types" is closed, meaning we can make the existential box "properly sized" and avoid allocating on the heap. Basically the memory footprint of an enum.
I agree with @miku1958 on this. Shouldn't it be the maximal common protocols? Swift already has the capability to find the most specific protocol conformance (see this thread). So it should have no problem in finding the maximal common protocols too, I think.
EDIT: Or did you forget to add P conformance to struct A and B in your example? In that case, I think compiler should throw error. If P.Assoc has a protocol conformance, however, z can be of existential type.
It was at first, before any mention of being able to perform any operation on a union other than typecast it. And it definitely was one of the several iterations proposed in the thread Xiaodi linked to, none of which have been embraced.
I’m not aware of any explanation of what is and is not considered out of bounds beyond the blanket dismissal in the “frequently rejected proposals” list. I think a deeper analysis would help people who want this feature understand what problems they need to consider and solve for before coming to the forums to ask again.
While it would work when all types conform to Error, this can't be true for all protocols. Protocols with static methods, initializers, or associated types as requirement can't be propagated to the union type.
For instance, Decodable has init(from decoder: Decoder), but in the absence of a self you can't decide which type's init will be used. In the absence of a proper implementation specifically for the union type, the Decodable conformance can't be propagated to the union. And funnily, while we could propagate the Encodable protocol to the union, it does not make much sense without a Decodable to match.
What would the behavior be if the most common ancestor type was a protocol with a requirement that involves Self or an associated type? Currently Swift doesn’t let such a protocol serve as the type of a value. This is the infamous “protocol can only be used as a generic constraint because it has self or associatedtype requirements” error. StackOverflow has a good explanation of why this restriction exists.
Please see my updated answer above. TLDR: if P.Assoc has a protocol conformance, existential type can be used; otherwise compiler should throw amibiguity error.
If that was a desirable solution, it would already be the behavior of all protocols with associated type, Self, or static requirements. Clearly, the language designers do not think it would be a good solution.
Array<String | Int> is fine just as Array<StringOrInt> is fine.
But I wouldn't expect inference from heterogeneous values to work without a hint:
// type inference from array values:
let a = [1, "a"] // !! this is an error today
let b = [2, "b"] as [Any] // the error message suggests this
let c = [3, "c"] as [String | Int] // this could also be suggested
Here too what is true for a protocol is also true for a union type. That's the way I see things.
Not entirely, and not in the way that OP proposed:
This way, a protocol or protocol extension member (method/property/subscript/initializer) may be used on an existential value unless:
The type of the invoked member (accessor — for storage declarations), as viewed in context of the base type, contains references to Self or Self-rooted associated types in non-covariant position.