Tuesday, November 28, 2023

Swift Proposal: Typed Throws

SE-0413:

Swift is known for being explicit about semantics and using types to communicate constraints that apply to specific APIs. From that perspective, the fact that all thrown errors are of type any Error feels like an outlier. However, it reflects the view laid out in the original error handling rationale that errors are generally propagated and rendered, but rarely handled exhaustively, and are prone to changing over time in a way that types are not.

[…]

The loss of information between types like Result and Task and the language’s error-handling system provides partial motivation for the introduction of typed throws, and is discussed further below.

Typed throws also provides benefits in places where clients need to exhaustively handle errors. For this to make sense, the set of potential failure conditions must be relatively fixed, either because they come from the same module or package as the clients, or because they come from a library that is effectively standalone and unlikely to evolve to (e.g.) pass through an error from another lower-level library. Typed throws also provides benefits in generic code that will propagate errors from its arguments, but never generate errors itself, as a more flexible alternative to the existing rethrows. Finally, typed throws also open up the potential for more efficient code, because they avoid the overhead associated with existential types (any Error).

Even with the introduction of typed throws into Swift, the existing (untyped) throws remains the better default error-handling mechanism for most Swift code.

Rob Napier (2016):

I was an early proponent of typed errors in Swift. This is how the Swift team convinced me I was wrong.

Strongly typed errors are fragile in ways that can lead to poor API evolution. If the API promises to throw only one of precisely 3 errors, then when a fourth error condition arises in a later release, I have a choice: I bury it somehow in the existing 3, or I force every caller to rewrite their error handling code to deal with it. Since it wasn’t in the original 3, it probably isn’t a very common condition, and this puts strong pressure on APIs not to expand their list of errors, particularly once a framework has extensive use over a long time (think: Foundation).

Of course with open enums, we can avoid that, but an open enum achieves none of the goals of a strongly typed error. It is basically an untyped error again because you still need a “default.”

[…]

Last of all, for strongly typed errors to be of much use, Foundation would need to throw them since it is the largest producer of errors in the system.

Previously:

Update (2024-02-23): Donny Wals:

At the time of writing this post SE-0413 has been accepted but not yet implemented.

[…]

Personally, I think typed throws are a nice feature but that we won’t see them used that much.

The fact that we can only throw a single type combined with having to try calls in a do block erasing our error back to any Error means that we’ll still be doing a bunch of switching and inspecting to see which error was thrown exactly, and how we should handle that thrown error.

Comments RSS · Twitter · Mastodon

Leave a Comment