> On Aug 18, 2017, at 3:28 AM, Charlie Monroe <[email protected]> wrote: >> On Aug 18, 2017, at 8:27 AM, John McCall via swift-evolution >> <[email protected]> wrote: >>> On Aug 18, 2017, at 12:58 AM, Chris Lattner via swift-evolution >>> <[email protected]> wrote: >>> Splitting this off into its own thread: >>> >>>> On Aug 17, 2017, at 7:39 PM, Matthew Johnson <[email protected]> >>>> 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 >> [email protected] >> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
