Hello all, I'm also on the "side" of untyped errors, but I can imagine how other developers may like a stricter error hierarchy. It surely fits some situations.
Enter Result<T> and Result<T, E>: Since Swift "native" errors don't fit well with asynchronous APIs, various ways to encapsulate them have emerged, most of them eventually relying on some kind of variant of those `Result` type: // Untyped errors enum Result<T> { case success(T) case failure(Error) } // Typed errors enum Result<T, E: Error> { case success(T) case failure(E) } The first Result<T> fits well people who like untyped errors. And Result<T, E> fits people who prefer typed errors. Result<T> is objectively closer to the "spirit" of Swift 2-4. Yet Result<T, E> has the right to live as well. When Swift 5 brings sugar syntax around async/await/etc, most needs for Result<T> will naturally vanish. However, the need for Result<T, E> will remain. The debate about "typed throws", for me, sums up to this question: will the typed folks be able to take profit from the syntax sugar brought by async/await/etc of Swift 5? Or will they have to keep on carrying Result<T, E> with them? Gwendal Roué > Le 18 août 2017 à 10:23, John McCall via swift-evolution > <swift-evolution@swift.org> a écrit : > >> On Aug 18, 2017, at 3:28 AM, Charlie Monroe <char...@charliemonroe.net >> <mailto:char...@charliemonroe.net>> wrote: >>> On Aug 18, 2017, at 8:27 AM, John McCall via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>> On Aug 18, 2017, at 12:58 AM, Chris Lattner via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>> Splitting this off into its own thread: >>>> >>>>> On Aug 17, 2017, at 7:39 PM, Matthew Johnson <matt...@anandabits.com >>>>> <mailto:matt...@anandabits.com>> wrote: >>>>> One related topic that isn’t discussed is type errors. Many third party >>>>> libraries use a Result type with typed errors. Moving to an async / >>>>> await model without also introducing typed errors into Swift would >>>>> require giving up something that is highly valued by many Swift >>>>> developers. Maybe Swift 5 is the right time to tackle typed errors as >>>>> well. I would be happy to help with design and drafting a proposal but >>>>> would need collaborators on the implementation side. >>>> >>>> Typed throws is something we need to settle one way or the other, and I >>>> agree it would be nice to do that in the Swift 5 cycle. >>>> >>>> For the purposes of this sub-discussion, I think there are three kinds of >>>> code to think about: >>>> 1) large scale API like Cocoa which evolve (adding significant >>>> functionality) over the course of many years and can’t break clients. >>>> 2) the public API of shared swiftpm packages, whose lifecycle may rise and >>>> fall - being obsoleted and replaced by better packages if they encounter a >>>> design problem. >>>> 3) internal APIs and applications, which are easy to change because the >>>> implementations and clients of the APIs are owned by the same people. >>>> >>>> These each have different sorts of concerns, and we hope that something >>>> can start out as #3 but work its way up the stack gracefully. >>>> >>>> Here is where I think things stand on it: >>>> - There is consensus that untyped throws is the right thing for a large >>>> scale API like Cocoa. NSError is effectively proven here. Even if typed >>>> throws is introduced, Apple is unlikely to adopt it in their APIs for this >>>> reason. >>>> - There is consensus that untyped throws is the right default for people >>>> to reach for for public package (#2). >>>> - There is consensus that Java and other systems that encourage lists of >>>> throws error types lead to problematic APIs for a variety of reasons. >>>> - There is disagreement about whether internal APIs (#3) should use it. >>>> It seems perfect to be able to write exhaustive catches in this situation, >>>> since everything in knowable. OTOH, this could encourage abuse of error >>>> handling in cases where you really should return an enum instead of using >>>> throws. >>>> - Some people are concerned that introducing typed throws would cause >>>> people to reach for it instead of using untyped throws for public package >>>> APIs. >>> >>> 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. >> >> The issue I see here with non-typed errors is that relying on documentation >> is very error-prone. I'll give an example where I've used exhaustive error >> catching (but then again, I was generally the only one using exhaustive enum >> switches when we discussed those). I've made a simple library for reporting >> purchases to a server. The report needs to be signed using a certificate and >> there are some validations to be made. >> >> This generally divides the errors into three logical areas - initialization >> (e.g. errors when loading the certificate, etc.), validation (when the >> document doesn't pass validation) and sending (network error, error response >> from the server, etc.). >> >> Instead of using a large error enum, I've split this into three enums. At >> this point, especially for a newcommer to the code, he may not realize which >> method can throw which of these error enums. >> >> I've found that the app can take advantage of knowing what's wrong. For >> example, if some required information is missing e.g. >> Validation.subjectNameMissing is thrown. In such case the application can >> inform the user that name is missing and it can offer to open UI to enter >> this information (in the case of my app, the UI for sending is in the >> document view, while the mentioned "subject name" information is in >> Preferences). >> >> This way I exhaustively switch over the error enums, suggesting to the user >> solution of the particular problem without dumbing down to a message "Oops, >> something went wrong, but I have no idea what because this kind of error is >> not handled.". > > Surely you must have a message like that. You're transmitting over a > network, so all sorts of things can go wrong that you're not going to explain > in detail to the user or have specific recoveries for. I would guess that > have a generic handler for errors, and it has carefully-considered responses > for specific failures (validation errors, maybe initialization errors) but a > default response for others. Maybe you've put effort into handling more > errors intelligently, trying to let fewer and fewer things end up with the > default response — that's great, but it must still be there. > > That's one of the keys to my argument here: practically speaking, from the > perspective of any specific bit of code, there will always be a default > response, because errors naturally quickly tend towards complexity, far more > complexity than any client can exhaustively handle. Typed throws just means > that error types will all have catch-all cases like MyError.other(Error), > which mostly seems counter-productive to me. > > I totally agree that we could do a lot more for documentation. I might be > able to be talked into a language design that expressly acknowledges that > it's just providing documentation and usability hints and doesn't normally > let you avoid the need for default cases. But I don't think there's a > compelling need for such a feature to land in Swift 5. > >> Alternatives I've considered: >> >> - wrapping all the errors into an "Error" enum which would switch over type >> of the error, which is not a great solution as in some cases you only throw >> one type of error > > That's interesting. In what cases do you only throw one type of error? Does > it not have a catch-all case? > >> - I could throw some error that only contains verbose description of the >> problem (generally a String), but I don't feel it's the library's job to >> stringify the error as it can be used for a command-line tools as well > > Absolutely. Using enums is a much better way of structuring errors than > using a string. > >> Perhpas I'm missing something, but dealing with UI and presenting an >> adequate error dialog to the user can be a challenge in current state of >> things given that currently, in the error catching, you fallback to the >> basic Error type and generally don't have a lot of options but to display >> something like "unknown error" - which is terrible for the user. > > Again, it comes down to whether that's ever completely avoidable, and I don't > think it is. Good error-handling means doing your best to handle common > errors well. > > John. > >> >>> >>> Sometimes we then go on to have a conversation about wrapping errors in >>> other error types, and that can be interesting, but now we're talking about >>> adding a big, messy feature just to get "safety" guarantees for a fairly >>> minor need. >>> >>> Programmers often have an instinct to obsess over error taxonomies that is >>> very rarely directed at solving any real problem; it is just self-imposed >>> busy-work. >>> >>>> - Some people think that while it might be useful in some narrow cases, >>>> the utility isn’t high enough to justify making the language more complex >>>> (complexity that would intrude on the APIs of result types, futures, etc) >>>> >>>> I’m sure there are other points in the discussion that I’m forgetting. >>>> >>>> One thing that I’m personally very concerned about is in the systems >>>> programming domain. Systems code is sort of the classic example of code >>>> that is low-level enough and finely specified enough that there are lots >>>> of knowable things, including the failure modes. >>> >>> Here we are using "systems" to mean "embedded systems and kernels". And >>> frankly even a kernel is a large enough system that they don't want to >>> exhaustively switch over failures; they just want the static guarantees >>> that go along with a constrained error type. >>> >>>> Beyond expressivity though, our current model involves boxing thrown >>>> values into an Error existential, something that forces an implicit memory >>>> allocation when the value is large. Unless this is fixed, I’m very >>>> concerned that we’ll end up with a situation where certain kinds of >>>> systems code (i.e., that which cares about real time guarantees) will not >>>> be able to use error handling at all. >>>> >>>> JohnMC has some ideas on how to change code generation for ‘throws’ to >>>> avoid this problem, but I don’t understand his ideas enough to know if >>>> they are practical and likely to happen or not. >>> >>> Essentially, you give Error a tagged-pointer representation to allow >>> payload-less errors on non-generic error types to be allocated globally, >>> and then you can (1) tell people to not throw errors that require >>> allocation if it's vital to avoid allocation (just like we would tell them >>> today not to construct classes or indirect enum cases) and (2) allow a >>> special global payload-less error to be substituted if error allocation >>> fails. >>> >>> Of course, we could also say that systems code is required to use a >>> typed-throws feature that we add down the line for their purposes. Or just >>> tell them to not use payloads. Or force them to constrain their error >>> types to fit within some given size. (Note that obsessive error taxonomies >>> tend to end up with a bunch of indirect enum cases anyway, because they get >>> recursive, so the allocation problem is very real whatever we do.) >>> >>> John. >>> _______________________________________________ >>> 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