For me, the main benefit of Type Unions would be dealing with Type errors.
I know this has been rejected, but now that we have full typed errors within Swift, we may need to start to consider Type Unions again.
For example, currently, if you’re working with async code, you will likely want to handle CancellationError
as thrown by Task.sleep
and Task.checkCancellation()
, so if you adopt any typed throws, you need to be able to deal with a union of CancellationError | MyCustomError
.
To be explicit about this:
Sure, you could wrap any call to a Task method that may throw and capture the CancellationError
then places it within a custom MyUnionErrorContainer
with a case for your errors, etc.; however, this falls apart as soon as you need to use other Task-related operations like withThrowingTaskGroup
.
However, if you were to wrap the CancillationError in a custom enum, then the standard lib would have no way to extract the CancillationError from this and would rethrow your wrapped error (cancelling all other tasks).
Exception handling, it is extremely common for a third-party library (or system lib) to handle some exceptions but re-throw others, so this is a very common pattern.
If we want typed errors to be adopted, and we want the standard library to be able to expose them (eg have a ThrowingTaskGroup that throws a typed error from its content), then we need to be able to have a Union of types on these errors. The same is true for many other standard library methods that currently do not support typed errors, as while most of the time they just re-throw the wrapped closure, they may sometimes throw their own type or swallow some exception thrown from the wrapped function.
Key to this woudl be to ensure the Type Union system is able to support generic types as well;
For cases were a function may throw an additional error to those in the closure it is handling
func decodeComponents<E: Error>(_ block: (String) throws(E) -> Int) -> throws(E | DecodingError) -> [Int]
and for cases were a method swallow an error:
func search<E: Error>(_ block: (String) throws(E | CancelSearchError) -> Bool) -> throws(E) -> Int?