[swift-evolution] Proposal: Typed throws
rjmccall at apple.com
Fri Dec 4 13:08:26 CST 2015
> On Dec 4, 2015, at 10:18 AM, Adrian Kashivskyy <adrian.kashivskyy at me.com> wrote:
> Anyone has any thoughts on this? I didn't expect this topic to die without any response…
Patience. :) We’ve been getting a lot of proposals, and it hasn’t even been a full day yet.
I do think that there’s a place for typed throws. There are some very narrow use cases, usually where error handling is being employed as a sort of alternate control scheme rather than a mechanism for reporting “errors” per se, where it’s nice to both:
- inform callers that the function only “fails” in the prescribed ways
- statically enforce that general errors aren’t accidentally getting mixed in
You might imagine using it in a recursive-descent parser, for example, although even there I’m not convinced.
However, I’m still reluctant to fully embrace the feature, because I think it’s far too tempting to try to use it for actual error handling, and that’s a job that it’s really, really poorly-suited for.
Let’s get one thing out of the way first. There are some operations that make sense to be able to do on a generic error — crucially, formatting it for display in various ways — and right now the ErrorType protocol doesn’t expose API for that. That’s something we perhaps need to work on.
Beyond that, however, it has always been unclear to me what exactly programmers expect to do with this additional typing information on errors, besides pedantically copying it all over the place and feeling pleased. Libraries can fail in a lot of different ways. It’s inevitable that that set will grow in ways that neither the library’s authors nor its users are necessarily anticipating. Even putting evolution aside, the list of failures is almost certainly impractical to exhaustively pattern-match over, and doing so couples you very tightly to the implementation of the library and everything it uses. Furthermore, in practice nearly every “exhaustively enumerable” error type I’ve ever seen has at least one (and often two or three) generic, catch-all cases used to encode arbitrary extra kinds of error, either dynamically-typed (e.g. carrying an ErrorType) or completely untyped (e.g. carrying a String); and not only does this make it impossible to exhaustively enumerate the actual kinds of failure, but it actually makes it more difficult to detect specific failures, by making it more likely that the error you’re actually trying to detect and special-case will be stored at the end of a rather complicated path of wrapping errors.
And this isn’t even just about third-party libraries, because if there’s any lesson from the last fifty years of experience that's broadly applicable across programming languages, it’s that you really do need to think about how your program breaks down into components, and understand how those components work with each other essentially as if they were separable libraries, or else you will always, always run into a wall as you find that the complexity of your comprehension has failed to scale with the complexity of your code.
So I continue to feel that the pattern of “recognize a few special cases and then be prepared to deal with other failures generically” is basically the right approach for ordinary error-handling in Swift, and I think it’s pretty well-served by the current design.
More information about the swift-evolution