[swift-evolution] Throws? and throws!

Xiaodi Wu xiaodi.wu at gmail.com
Thu Jan 12 19:24:38 CST 2017


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

>
> On Jan 12, 2017, at 3: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 think that is an invalid assumption.  I don’t think we should limit our
> proposals based on the performance of the naive/brute-force implementation.
> I am sure there are lots of tricks the compiler could pull if performance
> is an issue. Off the top of my head, although the user model is that it
> adds “try!” before the function, it could actually make two different
> versions of the function (one with fatalError and the other with error
> handling) and insert the right one based on how it is called.
>
>
> 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.
>
>
> I would argue that it allows the framework designer a lot of
> expressiveness about what they feel the correct approach is.  By using
> ‘throws’, ‘throws?’, or ‘throws!’, they are able to require explicitness or
> not depending on how they expect the framework to be used.  There is still
> a lot of value to plain old ‘throws’ (namely where I need the user to
> consider the possibility of error), and I would continue to use it for most
> cases.  It is the cases where I have been forced to use fatalError, where I
> would use ‘throws!’.
>
> While mistake prevention is a worthy goal which I support fully, there is
> a difference between preventing programming errors and trying to enforce
> one’s coding philosophy on everyone.  I would argue for things like this,
> we should favor expressiveness and then trust framework designers to use
> that expressiveness responsibly.  I might argue differently in cases where
> there was a high probability of foot-gunning, but I don’t feel like this is
> one of those cases.  It is a choice made by the framework designer (just
> like they can make a class ‘open’ or not), and the good designers will use
> it well.
>

I should add, with respect to this point: I highly agree that "high
probability of foot-gunning" is one criterion for caution. But I think
there are other valid reasons for caution, and one has been perhaps only
occasionally remarked upon on this list:

One discussion--I can't recall whether on this list or on
swift-users--showed that there was already some confusion about best
practices surrounding failable vs throwing functions; those who raised the
point argued that two ways of failing was one too many. The Error Handling
Rationale document makes a very good case for each holding its own weight,
but I think we can all see that the choice does add to the complexity of
good design in Swift.

As to your analogy, I was not one of the vociferous crowd to decry `open`,
but I did comment negatively on the idea that its spelling should resemble
yet another access level (as opposed to a modifier on `public`). I do
believe the response to that comment was that it was actually a wonderful
idea and that `open` should absolutely be its own access level. Yet, since
Swift 3, it has been mentioned on this list by more than one person that
five access levels is a few too many.

Recently, I read a series of articles comparing Swift and Rust, written by
someone who was proficient in Rust and in the process of learning Swift.
One overarching theme he returned to was that Swift seems to like to invent
new syntax for each new use case, which sometimes made for cleaner code and
sometimes meant simply yet another syntax to learn. (On the flip slide,
there's some commentary on Hacker News just today where people are
critiquing Rust's syntax for seeming a little too much like hieroglyphics.)

This is all to say that I do believe that, while expressiveness is
laudable, merely increasing options lives in tension with the goal of
having an approachable language that offers rich but _opinionated_ options.
It is that balance that we are debating here.

I would also add that all of your arguments apply equally well to ‘as!’ and
> forced unwrapping using ‘!’ which are both things that swift includes.
> Thus I don’t think it is a overarching design principle of swift as you
> claim.  Rather we have safety by default, with the ability to override.
>
>
> 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.
>
>
> On the contrary, it is on the roadmap, and thus is something we should
> plan for.  If we consider each feature only in isolation then we will end
> up with a fractured mess like C++.
>
>
> 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/45593a1b/attachment.html>


More information about the swift-evolution mailing list