> 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 <mailto:swift-evolution@swift.org>> wrote:
> 
> 
> Sent from my iPad
> 
> On Aug 18, 2017, at 1:27 AM, John McCall <rjmcc...@apple.com 
> <mailto:rjmcc...@apple.com>> 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.
> 
> 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.

> 
> 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 <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to