[swift-evolution] Proposal: Typed throws
John McCall
rjmccall at apple.com
Fri Dec 4 13:54:53 CST 2015
> On Dec 4, 2015, at 11:36 AM, David Owens II <david at owensd.io> wrote:
> 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…”) }
But this is printing. Of course you should be able to generically display an error, but you don’t need static typing for that. Also, I certainly hope you are not actually repeating all this stuff at every catch site.
>> 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.
This is abstract reasoning without any real consideration for what it’s going to do to actual code. My argument here is that it’s very easy to fall into a trap where you think this information is useful, but in practice it just leads to a lot of redundancy and boilerplate which, overall, actually means you’re handling errors *worse* than you were before.
> 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?
Yes. It is absolutely the case that libraries are not going to commit to a generating a small, fixed set of failures. Please feel free to try to find an exhaustive error enum that isn’t littered with vague catch-alls. Library authors often don’t want to commit to their current set of failures precisely *because* they feel that their current errors are too loosely typed, and they’re holding out hope that some day they’ll come along and make them more specific.
It’s nice to say things like library authors should be encouraged to think more carefully about what errors they generate, and I don’t disagree, but it is not reasonable language design to demand and expect that programmers suddenly start putting vastly more effort into this sort of thing. Honestly, I am curious what environment people are thinking of when they say that it’s been successful elsewhere, because whenever I see something like Result<T, SomeErrorType>, I take a look at SomeErrorType and almost always find that it is extremely loosely specified.
John.
> 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