[swift-evolution] Throws? and throws!

Xiaodi Wu xiaodi.wu at gmail.com
Thu Jan 12 18:46:54 CST 2017


On Thu, Jan 12, 2017 at 6:27 PM, Jonathan Hull <jhull at gbis.com> wrote:

> The problem with Java (and ObjC) is that exceptions can propagate
> unexpectedly into scopes which they weren’t expected and you end up with
> objects in inconsistent states where it is nearly impossible to continue in
> any meaningful way.  That can’t happen here as the error will never
> propagate beyond where it is expected to be.  On the contrary, ‘throws!’
> and ‘throws?’ both stop the propagation earlier than ‘throws’.
>

That's a fair point about `throws!`.

It seems that your motivation for `throws!` is to avoid writing `try!` at
the call site, since the functionality is otherwise the same. That keyword,
notably, is required at most once per statement. So--unless you're trying
to build up a solution for a whole new category of error (as mentioned
above)--the question boils down to: does having to write `try!` offer four
letters' worth of benefit at the call site? Your answer, if I understand,
seems to be: sometimes it might not be worth four letters, and that should
be enough to justify a new feature. I think, to make a convincing case,
you'll have to offer real-world use cases where many can agree on that
point, sufficient to justify the engineering resources for such a feature.
IMO the mere possibility that such a use case may exist is not enough,
especially offset against what I would argue is a real benefit to readers
of code that every call to a throwing function is currently clearly
annotated.

As to `throws?`, I'm not sure quite sure how it is supposed to work. If a
function `throws? -> Int`, is the return value actually of type `Int?`? And
if it's `throws? -> Int?`, then is the return value actually of type
`Int??`? In what ways would this design be superior to the already sketched
out `Result<T>` type outlined in the Error Handling Rationale document,
which several people on this list have lined up to propose? Would a
`Result<T>` cover even your use cases for `throws!`?

Also, ‘try’ is still required to explicitly mark a potential error
> propagation point, which is what it was designed to do.  You don’t have
> ‘try’ with the variants because it is by default no longer a propagation
> point (unless you make it one explicitly with ’try’).
>

If this is quite safe and more convenient, why then shouldn't it be the
behavior for `throws`? (That is, why not just allow people to call throwing
functions without `try` and crash if the error isn't caught? It'd be a
purely additive proposal that's backwards compatible for all currently
compiling code.)

Thanks,
> Jon
>
> On Jan 12, 2017, at 3:50 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> Some additional thoughts: if I recall correctly from my (limited) Java
> days, throwing in Java worked essentially like your proposed `throws!`.
> That the Swift model expressly deviates from that example was not by
> accident, as far as I can tell. According to the error handling docs in the
> Swift repo:
>
> "Once an error is thrown, Swift will automatically propagate it out of
> scopes (that permit it), rather than relying on the programmer to manually
> check for errors and do their own control flow. This is just a lot less
> boilerplate for common error handling tasks. However, doing this naively
> would introduce a lot of implicit control flow, which makes it difficult to
> reason about the function's behavior. This is a serious maintenance problem
> and has traditionally been a considerable source of bugs in languages that
> heavily use exceptions.
>
> "Therefore, while Swift automatically propagates errors, it requires that
> statements and expressions that can implicitly throw be marked with the
> `try` keyword."
>
> So I think what this is saying is that requiring `try` _every time_ is
> core to the design of Swift error handling, and that `throws!` or even
> `throws?` would undo it.
>
>
> On Thu, Jan 12, 2017 at 5:35 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>> On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> I really like swift’s error handling system overall. It strikes a good
>>> balance between safety and usability.
>>>
>>> There are some cases where it would be nice to throw errors, but errors
>>> are rarely expected in most use cases, so the overhead of ‘try’, etc… would
>>> make things unusable. Thus fatalError or optionals are used instead.  For
>>> example, operators like ‘+’ could never throw because adding ’try’
>>> everywhere would make arithmetic unbearable. But in a few cases it would
>>> make my algorithm much cleaner if I just assume it will work and then catch
>>> overflow/underflow errors if they happen, and resolve each of them with
>>> special cases.  Or perhaps I am dealing with user entered values, and want
>>> to stop the calculation and display a user visible error (e.g. a symbol in
>>> a spreadsheet cell) instead of crashing.
>>>
>>
>> Unless I'm mistaken, there is a performance overhead for throwing
>> functions, and thus a much greater barrier to the use cases outlined above
>> is that the performance penalty for '+' would be unacceptable in any case,
>> whatever syntactic sugar you could come up with.
>>
>>
>>> I would like to propose adding ‘throws?’ and ‘throws!’ variants to
>>> ‘throws’.
>>>
>>> These would be used for cases where error handling is not the default
>>> desired behavior, but having it as an option is desired occasionally.
>>
>>
>> While I admit the idea has an appeal on a practical level, I have an
>> uncomfortable feeling about this. It's by definition true that error
>> handling is never the default desired behavior, and if I recall correctly
>> the performance of Swift error handling is tuned on the assumption that not
>> throwing is far more common than throwing. If we accept this statement at
>> face value as the justification for including `throws!`, then essentially
>> all `throws` should be `throws!`. And indeed I suspect that if the feature
>> were be implemented, that would rapidly become the case in much written
>> Swift. In essence, then, I think you're effectively proposing to invert the
>> assignment of responsibility for determining how errors are handled from
>> the call site to the declaration site, at least by default. It goes against
>> an overarching design principle in Swift (discussed earlier in the thread
>> about DefaultConstructible) not to provide such defaults and to require
>> explicitness at the call site.
>>
>> Essentially, the user would no longer have to preface the call with
>>> ‘try’, as the compiler would implicitly add ‘try?’ or ‘try!’ respectively.
>>>
>>> Thus, the function would act like a non-throwing function (either
>>> trapping or returning an optional in the case of error), but the user could
>>> add ‘try’ to the call to override that behavior and deal with the error
>>> more explicitly.
>>>
>>> Another example would be bounds checking on arrays.  If subscripting
>>> arrays was marked as ‘throws!’ then it would have the same default behavior
>>> it does now (trapping on bounds error).  But a user could add ‘try?’ to
>>> return nil for a bounds error in cases where they explicitly want that, or
>>> they could add ‘try’ to deal with it as an error using do-catch.
>>>
>>
>> Subscripts cannot throw at all at the moment, and in another thread some
>> of the challenges for designing throwing subscripts were just mentioned. I
>> suspect any sort of throwing subscript will not be possible in the
>> immediate future, so the argument for `throws!` here is moot.
>>
>> I think this would really increase the availability of error handling in
>>> areas where it is impractical right now…
>>>
>>
>> I think the practical argument could be made stronger by use cases not
>> encumbered by other difficulties as outlined above. Is there a currently
>> throwing function you've encountered that would be greatly improved by such
>> a feature? Besides that, though, my discomfort is (as mentioned above) that
>> the practical effect of such a feature is that it will actually altogether
>> invert the default responsibility for error handling, and I'm not sure that
>> it's entirely consistent with Swift's design as a consequence. In any case
>> it'd be a far greater change than you've made it out to be. Interesting
>> suggestion, definitely.
>>
>> Thanks,
>>> Jon
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170112/326ff57c/attachment.html>


More information about the swift-evolution mailing list