Sent from my iPad

> On Aug 18, 2017, at 11:24 PM, John McCall <rjmcc...@apple.com> wrote:
> 
> 
>>> On Aug 18, 2017, at 11:43 PM, Mark Lilback <m...@lilback.com> wrote:
>>> 
>>> 
>>> On Aug 18, 2017, at 2:27 AM, John McCall via swift-evolution 
>>> <swift-evolution@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.
> 
> I'm not saying people only log errors instead of handling them in some more 
> reasonable way.  I'm saying that logging functions are the only place I've 
> ever seen someone switch over an entire error type.
> 
> I keep bringing exhaustive switches up because, as soon as you have a default 
> case, it seems to me that you haven't really lost anything vs. starting from 
> an opaque type like Error.

If the bar for typed errors is going to be exhaustive handling without an 
"other" / "unknown" then I doubt we will be able to meet it.  That is possible 
and useful in some kinds of code but will certainly continue to be the 
exception.  

On the other hand, if the bar is more generally whether typed errors can 
improve error handling in practice to a sufficient degree to justify the 
feature I think there is a good chance that they can, given the right design.  

Currently good error handling often requires a lot of time reading 
documentation, and often a lot of time trying to *find* the right 
documentation.  All too often that documentation doesn't even exist or is 
spotty and out of date.  There has been more than one occasion where the only 
way to get the information I needed was to read the source code of a dependency 
(thankfully they were open source).  A language is obviously not expected to 
solve the problem of poor documentation, but it can and should help surface 
more information regardless of the state of documentation.

Once you have the necessary information it is often necessary to write tedious 
error analysis code to categorize the error appropriately for the purpose of 
recovery.

I believe these problems are a significant driver behind the sad state of error 
handling in many (probably most) apps.  If a library author believes they have 
sufficient information about usage to categorize errors in a way that will be 
useful in practice for the majority of their users the language should support 
the library author in doing this.  Again, this is specifically *not* about 
providing an exhaustive list of every possible kind of error that might occur. 
It is about categorizing errors based on anticipated recovery strategy (while 
still retaining the underlying error information for cases where the catch site 
requires it).

Types seem like a convenient way to accomplish the goal of categorization.  
They are also already in use by at least some of us, but unfortunately the type 
information is currently discarded by the language.  This is unfortunate for 
callers and can also lead to bugs where an error that should have been 
categorized leaks out because it wasn't wrapped as intended.

> 
>>> On Aug 18, 2017, at 3:11 PM, Matthew Johnson via swift-evolution 
>>> <swift-evolution@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
>> }
> 
> Okay.  I mean, this seems essentially like Swift's Error design.  The comment 
> says you use this type ubiquitously in your project.  The type completely 
> erases any differences between functions in terms of what errors they can 
> throw.  Predictably, it includes an unknown case.  Code that processes values 
> of this type must look essentially exactly like switching over an Error, 
> except that the unknown case involves explicitly matching '.unknown' instead 
> of using 'default'.
> 
>> //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)
>> }
> 
> Okay.  So you're doing semantic tagging of errors — you're communicating out 
> that an error arose during a specific operation.  And having some static 
> enforcement here makes it easier to ensure that you've tagged all the errors.
> 
>> 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.
> 
> I see.  So you do have some functions that use a more specific type.
> 
>> 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.
> 
> Do you really check for eight different cases at the call sites that get a 
> NetworkingError, or is there a common predicate that you use?  Is there a 
> reason that predicate cannot be written on Error?
> 
>> 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.
> 
> Can you talk about about why "without using the runtime" is an important 
> requirement to you?  Why is an enum-switch with a catch-all case acceptable 
> but a type-switch with a default unacceptable?
> 
>> 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.
> 
> 
> John.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to