> On Feb 28, 2017, at 11:08 AM, Vladimir.S <sva...@gmail.com> wrote: > > On 28.02.2017 19:48, Matthew Johnson wrote: >> >>> On Feb 28, 2017, at 6:47 AM, Vladimir.S <sva...@gmail.com> wrote: >>> >>> On 28.02.2017 0:40, Matthew Johnson via swift-evolution wrote: >>>> >>>>> On Feb 27, 2017, at 1:46 PM, David Waite via swift-evolution >>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>> >>>>> IMHO, there are two kinds of responses to errors - a specific response, >>>>> and a general one. Only the calling code knows how it will deal with >>>>> errors, so a “typed throws” is the function guessing possible calling >>>>> code behavior. >>>>> >>>>> The problem is, that gives four possible combinations - two where the >>>>> function guesses correctly, and two where it doesn’t. The most damaging >>>>> is when it suspects a caller doesn’t care about the error, when the >>>>> caller actually does. This is unwanted wrapping. >>>>> >>>>> To provide an example, imagine a library that parses JSON. It has several >>>>> errors indicating JSON syntactic errors, and an “other” for representing >>>>> errors on the input stream. It wraps the input stream errors so that it >>>>> can provide a closed set of errors to the caller. >>>>> >>>>> The caller is responsible for returning a data set. It doesn’t think that >>>>> code calling ‘it” cares about JSON syntactic errors, merely that the >>>>> object was not able to be restored. It returns its own wrapped error. >>>>> >>>>> However, the original caller knows it is loading from disk. If the >>>>> problem is due to an issue such as access permissions, It has to know >>>>> implementation details of the API it called if it wishes to dive through >>>>> the wrapped errors to find out if the problem was filesystem related. >>>>> >>>>> Add more layers, and it can be very mysterious why a call failed. Java at >>>>> least captures stack traces in this case to aid in technical support in >>>>> diagnosing the error. >>>>> >>>>> Wrapping exceptions also prevents an aggregate of errors from different >>>>> subsystems being handled as a category, such as having a catch block >>>>> handle RecoverableError generically >>>> >>>> Fwiw, I think wrapping errors is something that people are sometimes going >>>> to want to do regardless of whether they are typed or not. Maybe the >>>> solution is to better support wrapping errors by focusing on the problems >>>> that wrapping causes. For example, we could do something like this to make >>> >>> Just to clarify, do you think about something like this? : >>> (pseudocode, sorry for mistakes) >> >> Is the question about the ability to create `fullErrorStackDescription`? >> Yes, that would certainly be possible to implement in an extension. >> >> I’m not sure why your examples use `rethrow` to rethrow the error instead of >> `throw`. Was that a mistake? >> >> I also want to reiterate that deciding when and how to wrap errors is >> something that requires careful thought and judgment. The goal is to >> provide a stable and ergonomic way for callers to learn about and handle >> errors they are likely to care about. There will often be an “everything >> else” case and that’s ok. >> >> As others have pointed out, the important thing is that it is easy to >> identify cases where I can *improve* UX by recovery or specific messaging to >> the user. The fact that there will usually be a default path is a given, >> but it’s best to avoid that path if possible. >> > > Well, I was trying to figure out how the some code could looks like with > typed throws, taking into account your suggestion about built-in > underlyingError and originalError props in Error protocol and the idea of > "Maybe the solution is to better support wrapping errors by focusing on the > problems that wrapping causes". > As for 'rethrow' it is not a mistake, but hypothetical use of the keyword to > rethrow underlying error inside own error without boilerplate code, i.e. > "better support wrapping", compare: > > catch let e { > // need to somehow inject the current e instance into our error instance > // (or with some other syntax) > throw BarError.fooRelatedError(SomeType1(), underlying: e) > } > > and > > catch { > // 'rethrow' can clearly say that the current error instance will be injected > // into our own error instance in underlyingError prop. we focuse only > // on our own error instance > rethrow fooRelatedError(SomeType1()) > } > > Sorry for not clarifying all this in first message.
Ahh, ok. I think the best way to accomplish something like this would be with some kind of general error propagation, as has already been discussed. With a mechanism like that you wouldn’t have to catch the error at all when you’re just wrapping it. > >>> >>> func foo() throws {} >>> >>> func bar() throws {} >>> >>> enum BazError: Error { case baz1, case baz2 } >>> func baz() throws(BazError) {..} >>> >>> enum BatError: Error { >>> case fooRelatedError(SomeType1) >>> case barOrBazRelatedError >>> case specialErrorOne(Int) >>> case specialErrorTwo(String) >>> } >>> >>> func bat() throws(BatError) { >>> do { >>> try foo() >>> } >>> catch { >>> // underlyingError will be injected >>> rethrow .fooRelatedError(SomeType1()) >>> } >>> >>> do { >>> try bar() >>> try baz() >>> } >>> catch { >>> // underlyingError will be injected >>> rethrow .barOrBazRelatedError >>> } >>> >>> .. >>> if flag1 { throw .specialErrorOne(intValue) } >>> .. >>> if flag2 { throw .specialErrorTwo(stringValue) } >>> .. >>> } >>> >>> and then >>> >>> func test() { >>> do { >>> ... >>> try bat() >>> ... >>> } >>> catch let e as BatError { >>> switch e { >>> case fooRelatedError(let some) : { print(some, e.underlyingError) } >>> case barOrBazRelatedError : { >>> print("something with bar or baz, so try this:..") >>> >>> if let bazError = e.underlyingError as BazError { >>> switch bazError {.....} >>> } else { >>> // do something about "bar" error >>> } >>> } >>> case specialErrorOne(let i) : { print(i) } >>> case specialErrorTwo(let s) : { print(s) } >>> } >>> >>> log(e.fullErrorStackDescription) // BatError.description + >>> underlyingError.description + underlyingError.underlyingError.description >>> etc >>> }... >>> } >>> >>> ? >>> >>>> it easier to get at the original error: >>>> >>>> protocol Error { >>>> // The error directly underlying this error. >>>> // Ideally the compiler would synthesize an implementation for enums >>>> conforming to `Error` >>>> // If `self` is a case that has an associate value which is or conforms >>>> to `Error` that error would be returned, otherwise `nil` would be returned. >>>> var underlyingError: Error? { get } >>>> >>>> // The original error underlying *all* layers of wrapping. >>>> // If underlyingError is non-nil this is also non-nil. >>>> var originalError: Error { get } >>>> } >>>> extension Error { >>>> var underlyingError: Error? { >>>> return nil >>>> } >>>> var originalError: Error { >>>> return underlyingError?.originalError ?? underlyingError ?? self >>>> } >>>> } >>>> >>>> We could even provide syntactic sugar for catch sites that want to deal >>>> with the original error rather than the wrapped error if that is an >>>> important use case. >>>> >>>>> >>>>> An interesting solution that has emerged in Ruby to keep library authors >>>>> from wrapping exceptions is by decorating the existing exception. >>>>> Exceptions are caught via pattern matching (same as in Swift), so rather >>>>> than wrap an extension, they extend the error instance with a >>>>> library-specific module (e.g. swift protocol). So while the error may be >>>>> a IOError in ruby, you can still catch it via ‘rescue JSONError’ >>>>> >>>>> Trying to specify the exact errors becomes even more destructive with >>>>> protocols and closures, where the person defining the interface knows >>>>> neither which errors the implementor of the call will throw, nor >>>>> necessarily if the caller will want to implement specific behavior on >>>>> those errors. This in my personal Java coding experience almost always >>>>> leads to wrapping in some protocol-specific Exception type which exposes >>>>> minimal information to the caller, or exposing your errors in some >>>>> unrelated type like IOException which was declared based on the author’s >>>>> experience of possible exceptions. >>>>> >>>>> -DW >>>>> >>>>>> On Feb 27, 2017, at 5:19 AM, Daniel Leping via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> >>>>>> On Mon, 27 Feb 2017 at 8:44 Dave Abrahams via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> >>>>>> on Fri Feb 17 2017, Joe Groff <swift-evolution@swift.org >>>>>> <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> > Experience in other languages like Rust and Haskell that use >>>>>> > Result-based error propagation suggests that a single error type is >>>>>> > adequate, and beneficial in many ways. >>>>>> >>>>>> >>>>>> >>>>>> And experience in still others, like C++ and Java, suggests that >>>>>> using static types to restrict the kind of information a function can >>>>>> give you when an error occurs may actually be harmful. >>>>>> >>>>>> +1 here. It becomes wrapping over wrapping over wrapping. Try doing a >>>>>> big app in Java (i.e. some kind of layered server) and you'll understand >>>>>> everything. Ones who tried and still want it - well, there are different >>>>>> tastes out there. >>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> -Dave >>>>>> >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>> >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>> >>>>> _______________________________________________ >>>>> swift-evolution mailing list >>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> >>>> >>>> >>>> _______________________________________________ >>>> swift-evolution mailing list >>>> swift-evolution@swift.org >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> >> >> _______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution