[swift-evolution] Proposal: Typed throws
David Owens II
david at owensd.io
Fri Dec 4 13:36:39 CST 2015
For the most part, I feel that typed errors are more of a response to how we need to handle errors within Swift today.
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch { fatalError("this is always needed…”) }
> 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.
Possibly… but I think the counter argument is that introducing a new error type that can happen **should** be a breaking change. So the `catch` with no pattern becomes a pattern that is “yeah, some error may happen at a later date, but since we have no idea what it is, it may be benign or it may be catastrophic. So, should it be handled with a `fatalError()` or should it be equivalent to a no-op?
To me, the question really becomes: does a non-typed error-like system really make sense in Swift? Or in other words, if interop with ObjC was not required, would `throw` exist as it does today?
It would seem that something has to give: either something like a `Result`/`Error` type should be used instead with some nicer syntax to handle it, or `throws` should really become typed. Leaving it as-is, seems to be the worst of both worlds.
-David
> On Dec 4, 2015, at 11:08 AM, John McCall <rjmccall at apple.com> wrote:
>
>> 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.
>
> John.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
More information about the swift-evolution
mailing list