On Fri, Aug 18, 2017 at 6:19 PM, Matthew Johnson <matt...@anandabits.com> wrote:
> > On Aug 18, 2017, at 6:15 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: > > On Fri, Aug 18, 2017 at 09:20 Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote: > >> >> >> Sent from my iPad >> >> On Aug 18, 2017, at 1:27 AM, John McCall <rjmcc...@apple.com> wrote: >> >> >> On Aug 18, 2017, at 12:58 AM, Chris Lattner via swift-evolution < >> 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> >> 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. >> >> I agree that exhaustive switching over errors is something that people >> are extremely likely to actually want to do. I also think it's a bit of a >> red herring. The value of typed errors is *not* in exhaustive switching. >> It is in categorization and verified documentation. >> >> Here is a concrete example that applies to almost every app. When you >> make a network request there are many things that could go wrong to which >> you may want to respond differently: >> * There might be no network available. You might recover by updating the >> UI to indicate that and start monitoring for a reachability change. >> * There might have been a server error that should eventually be resolved >> (500). You might update the UI and provide the user the ability to retry. >> * There might have been an unrecoverable server error (404). You will >> update the UI. >> * There might have been a low level parsing error (bad JSON, etc). >> Recovery is perhaps similar in nature to #2, but the problem is less likely >> to be resolved quickly so you may not provide a retry option. You might >> also want to do something to notify your dev team that the server is >> returning JSON that can't be parsed. >> * There might have been a higher-level parsing error (converting JSON to >> model types). This might be treated the same as bad JSON. On the other >> hand, depending on the specifics of the app, you might take an alternate >> path that only parses the most essential model data in hopes that the >> problem was somewhere else and this parse will succeed. >> >> All of this can obviously be accomplished with untyped errors. That >> said, using types to categorize errors would significantly improve the >> clarity of such code. More importantly, I believe that by categorizing >> errors in ways that are most relevant to a specific domain a library >> (perhaps internal to an app) can encourage developers to think carefully >> about how to respond. > > > I used to be rather in favor of adding typed errors, thinking that it can > only benefit and seemed reasonable. However, given the very interesting > discussion here, I'm inclined to think that what you articulate above is > actually a very good argument _against_ adding typed errors. > > If I may simplify, the gist of the argument advanced by Tino, Charlie, and > you is that the primary goal is documentation, and that documentation in > the form of prose is insufficient because it can be unreliable. Therefore, > you want a way for the compiler to enforce said documentation. (The > categorization use case, I think, is well addressed by the protocol-based > design discussed already in this thread.) > > > Actually documentation is only one of the goals I have and it is the least > important. Please see my subsequent reply to John where I articulate the > four primary goals I have for improved error handling, whether it be typed > errors or some other mechanism. I am curious to see what you think of the > goals, as well as what mechanism might best address those goals. > Your other three goals have to do with what you term categorization, unless I misunderstand. Are those not adequately addressed by Joe Groff's protocol-based design? > > However, the compiler itself cannot reward, only punish in the form of > errors or warnings; if exhaustive switching is a red herring and the payoff > for typed errors is correct documentation, the effectiveness of this kind > of compiler enforcement must be directly proportional to the degree of > extrinsic punishment inflicted by the compiler (since the intrinsic reward > of correct documentation is the same whether it's spelled using doc > comments or the type system). This seems like a heavy-handed way to enforce > documentation of only one specific aspect of a throwing function; moreover, > if this use case were to be sufficiently compelling, then it's certainly a > better argument for SourceKit (or some other builtin tool) to automatically > generate information on all errors thrown than for the compiler to require > that users declare it themselves--even if opt-in. > > > Bad error handling is pervasive. The fact that everyone shows you code >> that just logs the error is a prime example of this. It should be >> considered a symptom of a problem, not an acceptable status quo to be >> maintained. We need all the tools at our disposal to encourage better >> thinking about and handling of errors. Most importantly, I think we need a >> middle ground between completely untyped errors and an exhaustive list of >> every possible error that might happen. I believe a well designed >> mechanism for categorizing errors in a compiler-verified way can do exactly >> this. >> >> In many respects, there are similarities to this in the design of >> `NSError` which provides categorization via the error domain. This >> categorization is a bit more broad than I think is useful in many cases, >> but it is the best example I'm aware of. >> >> The primary difference between error domains and the kind of >> categorization I am proposing is that error domains categorize based on the >> source of an error whereas I am proposing categorization driven by likely >> recovery strategies. Recovery is obviously application dependent, but I >> think the example above demonstrates that there are some useful >> generalizations that can be made (especially in an app-specific library), >> even if they don't apply everywhere. >> >> > 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. >> >> I think you're right that wrapping errors is tightly related to an >> effective use of typed errors. You can do a reasonable job without >> language support (as has been discussed on the list in the past). On the >> other hand, if we're going to introduce typed errors we should do it in a >> way that *encourages* effective use of them. My opinion is that >> encouraging effect use means categorizing (wrapping) errors without >> requiring any additional syntax beyond the simple `try` used by untyped >> errors. In practice, this means we should not need to catch and rethrow an >> error if all we want to do is categorize it. Rust provides good prior art >> in this area. >> >> > >> > 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. >> >> I agree that obsessing over intricate taxonomies is counter-productive >> and should be discouraged. On the other hand, I hope the example I >> provided above can help to focus the discussion on a practical use of types >> to categorize errors in a way that helps guide *thinking* and therefore >> improves error handling in practice. >> >> > >> >> - 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 >> https://lists.swift.org/mailman/listinfo/swift-evolution > > >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution