Proposal: Allow Type Annotations on Throws


(Anton Zhilin) #1

I want to bump the old thread
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/003284.html>
on "typed throws".

First of all, I just love the improvement from the caller point of view.
The following:

func updateData() throws DataUpdateError -> Data

do {
    let updates = try updateData()
    doTheUpdate(updates)
} catch .DownloadError {
    //...
} catch .InvalidError {
    //...
}

Is much better than the following:

func updateData() throws -> Data

do {
    let updates = try updateData()
    doTheUpdate(updates)
} catch DataUpdateError.DownloadError {
    //...
} catch DataUpdateError.InvalidError {
    //...
} catch {
    fatalError()
}

The main arguments for me are:
1. It is clear from the function definition, what errors will be thrown
2. The IDE also knows the error type and will help me as soon as I type the
dot
3. No catch-all boilerplate

Many people don't seem to understand the difference between Swift errors
and exceptions, that are like "the current task has failed, period". Swift
errors carry information that is enough to recover from the error close to
the throwing call. If it cannot be immediately recovered from, it should be
converted to appropriate kind of unchecked exception. That is why it is
useful and logical for the calling side to be informed of the whole range
of possible errors. In type-safe way, if possible.

Some developer teams already use this paradigm, although the language makes
it more difficult currently. It just disciplines error handling.

The difficulties begin at the "throwing side", where we have all these
error conversion issues.

I suggest that regardless of whether we do or do not find a way to resolve
those, we should add typed throws to the language. Developers of APIs that
use errors and want to simplify life for their users, will be able to do
so. If you don't want to mess with errors in your own code, continue to use
untyped 'throws'.

That's my main point. Additionally, I have a couple of (possibly dumb)
suggestions for error conversion.

Assume we have 'ErrorTwo(errorConversion:ErrorOne)' or other convention for
declaring convertion possibility. Then we have the following situation:

func foo() throws ErrorOne
func test() throws ErrorTwo {
    // how to call foo() ?
}

0. Current way (do-catch):

do {
    try foo()
} catch err as ErrorOne {
    throw ErrorTwo(errorConvertion: err)
}

Verbose.

1. Implicit error conversions:

try foo()

IMO, they are not as bad as implicit convertions would be in other parts of
the language. Just keep in mind that 'try' might convert its error type to
match 'throws' type of the current function.

2. 'try as'

try as ErrorTwo foo()

Uses existing keywords, but contains excessive information about the
resulting error type. What if that type was long?

3. Add a keyword or attribute for 'try' with conversion:

convertingtry foo()
converting try foo()
@converting try foo()

I prefer the attribute version.


Why doesn't Swift have explicit throwables like Java