[swift-evolution] typed throws
Mark Lilback
mark at lilback.com
Fri Aug 18 22:43:43 CDT 2017
>
> On Aug 18, 2017, at 2:27 AM, John McCall via swift-evolution <swift-evolution at swift.org> wrote:
>
> Even for non-public code. The only practical merit of typed throws I have ever seen someone demonstrate is that it would let them use contextual lookup in a throw or catch. People always say "I'll be able to exhaustively switch over my errors", and then I ask them to show me where they want to do that, and they show me something that just logs the error, which of course does not require typed throws. Every. Single. Time.
We're doing it in the project I'm working on. Sure, there are some places where we just log the error. But the vast majority of the time, we're handling them. Maybe that's because we're using reactive programming and errors bubble to the top, so there's no need to write that many error handlers. And if I am just logging, either the error doesn't really matter or there is a TODO label reminding me to fix it at some point.
> On Aug 18, 2017, at 3:11 PM, Matthew Johnson via swift-evolution <swift-evolution at swift.org> wrote:
>
> The primary goal for me personally is to factor out and centralize code that categorizes an error, allowing catch sites to focus on implementing recovery instead of figuring out what went wrong. Here’s some concrete sample code building on the example scenario above:
I'm using a similar approach. Here is some stripped down code:
//error object used throughout project
public struct Rc2Error: LocalizedError, CustomStringConvertible, CustomDebugStringConvertible {
/// basic categories of errors
public enum Rc2ErrorType: String, Error {
/// a requested object was not found
case noSuchElement
/// a requested operation is already in progress
case alreadyInProgress
/// problem parsing json, Freddy error is nested
case invalidJson
/// nestedError will be the NSError
case cocoa
/// nested error is related to the file system
case file
/// a wrapped error from a websocket
case websocket
/// a generic network error
case network
/// an invalid argument was passed (or parsed from json)
/// wraps an unknown error
case unknown
}
/// the generic type of the error
public let type: Rc2ErrorType
/// the underlying error that caused the problem
public let nestedError: Error?
/// location in source code of where error happened
public let location: String
}
//a domain-specific error type that will be nested
public enum NetworkingError {
case unauthorized
case unsupportedFileType
case timeout
case connectionError(Error)
case canceled
case uploadFailed(Error)
case invalidHttpStatusCode(HTTPURLResponse)
case restError(code: Int, message: String)
}
The most common errors don't need a nested error. The call site can figure out how to recover based on this. Using Result<T,E> I can specifically limit what kind of errors are possible from a function without using the wrapper error. E can always be specified as Error to ignore the typed system.
It would be great if the native swift error system let you optionally put compiler-enforced constraints on what kind of error can be thrown. Then I can setup handlers for my specific type of error, but the compiler will give an error/warning if I'm not handling a possible Error. A generic catch-all is not proper error handling. And if I'm calling something that doesn't throw type-constrained errors, I can use a generic handler and wrap it up in my own error. But the call site is getting details necessary to recover (if possible) without having to use the runtime to figure out what kind of error it is.
I've got a very efficient system set up right now with great error handling. I don't see why the same capability can't exist in the language, especially when you can choose to completely ignore it. Hopefully more people would use it and we'd stop seeing so many "unknown error" dialogs.
- Mark
More information about the swift-evolution
mailing list