> On Feb 28, 2017, at 12:29 AM, Karl Wagner <[email protected]> wrote:
>
>
>> On 28 Feb 2017, at 02:43, Matthew Johnson via swift-evolution
>> <[email protected] <mailto:[email protected]>> wrote:
>>
>>
>>
>> Sent from my iPad
>>
>> On Feb 27, 2017, at 7:19 PM, Daniel Leping <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>>> Well, as Dave pointed, you can very rarely recover from an error, which IMO
>>> is absolutely true.
>>>
>>> If your operation fails you don't really care unless you can recover. And
>>> you know your cases, which you can recover from (in reality one usually
>>> does it in optimization phase, though).
>>>
>>> What else do you need the type of error for at the very end of your call
>>> stack? In 90% you will tell the user "ah, sorry, something happened. Come
>>> back later" and log the error (if you haven't forgot it).
>>>
>>> In most cases the errors are not for recovering. They neither are to be
>>> presented to users. They are for developers to read the log/crash
>>> report/whatever else and analyze it. Most of the errors are for debugging
>>> purposes.
>>>
>>> I don't want to deal with cumbersome code the purpose of which is to just
>>> "obey the language rules". Unless I know how to recover I would rethrow it.
>>> Than catch at the top of the stack and log + show the user a nice "sorry"
>>> message without getting techy.
>>
>> In order to provide a helpful experience to end users an app needs to know
>> about what might have caused the error and therefore what might (or might
>> not) resolve it, allowing the operation to succeed on a subsequent attempt.
>> This can influence the strategy an app uses to avoid bothering the user if
>> it might be resolvable without user intervention and can also influence the
>> content of the message presented to users if that is necessary.
>>
>
> Errors are certainly useful, but the key distinction (IMO) is that callers
> should never rely on an error happening or not. They are typically highly
> implementation and hence version-specific. I think that means specific errors
> shouldn’t be part of the function signature.
>
> That doesn’t mean you can’t handle the error. A function that throws is one
> that reserves the right to fail; maybe you can resolve the problem, but maybe
> you can’t. That means your enclosing operation also needs to be prepared to
> fail for reasons beyond _its_ control, and so on. At some point your entire
> UI-level sub-operation (like opening the file, downloading the data from the
> network) just fails, and you decide whether or not the user needs to be told
> of that and how to do so.
It is important that when this happens you need to be able to give the user as
much useful information about what happened as possible.
>
>> Indeed, many apps don't bother with this kind of detail and just treat all
>> errors the same. Personally, I find that to be an unfortunate state of
>> affairs both as a user and as a developer.
>>
>> Types can be useful in conveying this kind of information and abstracting
>> low level details that are not helpful at higher levels in the stack. Of
>> course types are not the only way to convey this information. But my
>> experience is that untyped errors often result in libraries with poor
>> documentation of error cases and not enough consideration of the error
>> handling experience of users of the library in general. That makes it very
>> difficult to handle errors well. I have experiences like this in several
>> languages and on several platforms, including Apple's.
>
> I’m not sure that types are really the best abstraction for “the list of
> errors that can be thrown by this function”. They’re fine for error
> themselves, but not for per-function error-lists IMO. Most of our types are
> way too rigid for this to be easy to live with.
>
> If I understand what you’re saying, the core problem can be summarised as: "I
> don’t know/can’t easily communicate which errors this function throws”.
> Really, it is a documentation problem; it's too onerous to document every
> individual throwing function, even with our very pretty markdown-like syntax.
> I’m just not sure that the relatively rigid type-system is the solution.
If you have a better idea I would be thrilled to hear it, but documentation is
not a good enough answer IMO.
>
> Now, if we look at this from a documentation perspective: obviously, the
> compiler _typically_ can't generate documentation for you, because it doesn’t
> know what your types/functions are meant for. Error documentation, though, is
> different: the compiler *can* often know the specific errors which get thrown
> (or rethrown from a call to another function from whom it can get that
> information); and even when it can’t know that, often it can at least know
> the specific type of error. We could enhance the compiler libraries to track
> that; I spent half a day modifying the existing error-checker in Sema to
> prove the concept and it’s close.
But part of the point is that I *don’t* want to know every detail about errors
that can be thrown. I want to have a reasonable higher-level view of
*categories* of errors that might occur at runtime. I still want direct access
to the concrete error that actually occurred (i.e. the underlying error) at
runtime.
But I don’t want to be too tightly coupled to it and a full list of every
possible error is a point-in-time is not really relevant. It might include
information about dependencies of the library that I really shouldn’t know
about. For example, let’s say my library has a dependency on network library
XYZ and therefore it can throw XYZError. I don’t really want to know that or
match on it. It might later change to ABC network library that throws
ABCError. We need a stable abstraction that clients can depend on to reliably
catch network error as distinct from parsing errors, for example, if that
distinction is important to users of the library.
A type is required to allow the appropriate level of abstraction. The only
question is whether my library’s error type which gives users that stable
interface is visible in the type system or not. I think it should be. It
might be a resilient enum or a protocol and therefore not allow exhaustive
catch, but that is ok. The point is not exhaustive catch (although that’s nice
when possible). The point is to have a stable, well designed and considered
interface for errors coming out of the API that is clearly communicated to
users of that API.
>
> Swift libraries don’t have header files, so third-parties will only ever see
> your documentation as generated by some tool which integrates the compiler.
> That means the compiler can “improve” the documentation when it is lacking.
> For yourself, if you care about explicitly writing out the errors which get
> thrown and you’re not working with an IDE, you could enable compiler-checking
> of your documentation comments. We can invent new shorthands to make it
> easier to list all of your various errors (individual cases, entire types,
> and maybe some private “other errors”), isolated from the rest of the
> language. For example:
>
> /// - throws: MyError.{errorOne, errorThree}, AnotherError.*, *
> func doSomething() throws
>
> Maybe you can think of other ways we could help the documentation problem
> from the compiler side?
I don’t think this is the right approach at all. As noted above, this is
something that requires human judgement and design. The compiler cannot do
that for us. The fact that we often *don’t* do it is a problem and compiler
generated documentation is likely to make that problem worse.
>
>>
>> Typed errors are certainly no panacea but I believe they can be an important
>> tool. Experience in other languages has shown that to be the case for many
>> people writing many kinds of software.
>>
>> If you don't believe typed errors will improve your code or if you just
>> don't want to deal with typed errors, just don't use them! You will be able
>> to continue using untyped throws just as you do today. You can even do this
>> if you use libraries that throw typed errors.
>>
>
> If it’s the wrong solution to the problem, it does make the language worse
> overall.
Obviously we disagree here.
>
>>
>>>
>>> On Tue, 28 Feb 2017 at 1:12 Matthew Johnson via swift-evolution
>>> <[email protected] <mailto:[email protected]>> wrote:
>>>
>>> > On Feb 27, 2017, at 5:01 PM, Dave Abrahams <[email protected]
>>> > <mailto:[email protected]>> wrote:
>>> >
>>> >
>>> > on Mon Feb 27 2017, Matthew Johnson <matthew-AT-anandabits.com
>>> > <http://matthew-at-anandabits.com/>> wrote:
>>> >
>>> >>> On Feb 27, 2017, at 4:20 PM, Dave Abrahams <[email protected]
>>> >>> <mailto:[email protected]>> wrote:
>>> >>>
>>> >>>
>>> >>> I'm sorry, I don't see any substantive difference, based on what you've
>>> >>> written here, between this feature and const.
>>> >>
>>> >> Let me give it one more shot and then I’ll drop it. :)
>>> >>
>>> >> Const is viral because if an API does not declare its arguments const
>>> >> it cannot be used by a caller who has a const argument.
>>> >
>>> > Unless the caller casts away const, thus erasing information that was
>>> > previously encoded in the type system.
>>>
>>> Yes, of course.
>>>
>>> >
>>> >> It is required in order to make an API as generally useful as
>>> >> possible.
>>> >>
>>> >> Typed errors are not viral in this way because no callers are
>>> >> prevented from calling an API regardless of whether it declares error
>>> >> types or just throws Error like we have today.
>>> >
>>> > Unless the caller can recover (which is *very* rare) or it catches and
>>> > rethrows one of the errors *it* declares, thus erasing information that
>>> > was previously encoded in the type system.
>>>
>>> I view this as being fundamentally different than casting away const.
>>> Casting away const says “I know better than the types”.
>>>
>>> Converting an error to a different type is extremely different. It much
>>> more similar to any other kind of value wrapper a library might create in
>>> order to shield its users from being coupled to its implementation details
>>> / dependencies. This is not a way *around* the type system in the sense
>>> that casting away const is. It is a way of *using* the type system
>>> (hopefully) to your advantage.
>>>
>>>
>>>
>>> >
>>> >> Pressure to declare error types in your signature in order to make
>>> >> your function as generally useful as possible does not exist. Each
>>> >> function is free to declare error types or not according to the
>>> >> contract it wishes to expose.
>>> >>
>>> >> An argument can be made that community expectations might develop that
>>> >> good APIs should declare error types and they could be considered
>>> >> viral in this sense because any API that is simply declared `throws`
>>> >> is dropping type information. But I think this overstates the case.
>>> >> The community appears to be very sensitive to the problems that can
>>> >> arise when error types are too concrete, especially across module
>>> >> boundaries. I think we can learn to use the tool where it works well
>>> >> and to avoid it where it causes problems.
>>> >>
>>> >>>
>>> >>> --
>>> >>> -Dave
>>> >>
>>> >
>>> > --
>>> > -Dave
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution