Throws? and throws!


(Jon Hull) #1

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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon


(Xiaodi Wu) #2

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,

···

On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #3

Another place I can see this being useful is for higher-order functions which "obviously" work on a throwing operation. The most obvious example would be a constructor for a Result type that collects the success or error result from a throwing closure:

enum Result<T> {
  case ok(T)
  case error(Error)

  init(_ f: () throws -> T) {
    do {
      self = .ok(try f())
    } catch {
      self = .error(error)
    }
  }
}

Having to write `Result { try somethingThatMightFail() }` feels a bit silly because "obviously" the operation you pass to Result is going to be able to fail. Such a feature does feel like it could be easy to misuse, though.

-Joe

···

On Jan 12, 2017, at 2:58 PM, Jonathan Hull via swift-evolution <swift-evolution@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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #4

-1

I think the basic type system should remain free of such constructs.

I.e. if a Throws? is necessary on a + operation between integers, then the integers should be encapsulated into their own type.

In fact I am thinking lately of the “need” for a new type system in parallel to the current type system specifically for arithmetic. It would consist of Naturals, Reals, Imaginary etc. These new types would also have Nan and Infinite as members and use these to deal with out-of-bounds situations. Besides, it should be possible to constrain a subtype of them to a predefined ranges etc.

While these are just ‘thoughts’ at the moment, I did come to the conclusion that “one size fits all” is not the correct approach.
And I apply the same thinking to the suggested Throws?.
Imo it would introduce side effects into the current type system that would only benefit a small subset of users, and it would -probably- not completely satisfy those user fully. Hence a new arithmetic based type system would probably be better. And until (if ever) such a system is available it would imo be better to keep things as they are and implement specific mathematical requirements in wrappers.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

···

On 12 Jan 2017, at 23:58, Jonathan Hull via swift-evolution <swift-evolution@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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #5

I can see the appeal, but really throwable errors are just one kind of error that can happen in code.

Preconditions are a different thing. They represent axioms that must be kept for a sane program. It’s up to the programmer to decide whether something should be treated as an axiom or something recoverable. For a few particular operations (Array indexing, arithmetic), the standard library designers have decided that out-of-bounds values are to be considered axiom violations.

If that’s not convenient for your application, you could suggest changing the standard library to include throwing or optional-wrapped alternatives. You could also write your own:

extension Array {
    subscript(unchecked index: Index) -> Iterator.Element? { // Subscripts aren’t allowed to throw. There’s another proposal on that.
        guard (startIndex..<endIndex).contains(index) else { return .none }
        return self[index]
    }
}

let array = [1,2,3,4]
array[unchecked: 99] // Returns nil
array[unchecked: 0] // Returns 1

···

On 12 Jan 2017, at 23:58, Jonathan Hull via swift-evolution <swift-evolution@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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Russ Bishop) #6

I’m not sure I like this specific proposal, but the idea of having the option of catching a certain class of runtime errors is appealing.

I don’t think it makes sense to abort a server process (potentially dropping X threads and thousands of connections on the ground) because one stray thread performed an overflowing addition… one could always require use of addWithOverflow I suppose, but auditing code for “+” is a different kind of pain than auditing for UnsafeMutablePointer. For servers the operation is almost always bounded by a single request, so unwinding any transactions and aborting the thread is sufficient to recover. There will also be cases where cleanup on fatalError() is absolutely required; if Swift leaves dangling database transactions on abort it is not usable for a large number of scenarios (speaking from direct experience).

Rust takes an interesting approach with panic and catch_unwind… at compile time you can make a policy decision that all panics are aborts or that non-catastrophic panics can be handled, but only by the parent thread. In our terminology that would be saying that precondition ends the current dispatch work item and only the parent queue can handle the failure. This gets very tricky to even describe because it doesn’t map cleanly to libDispatch and we don’t have the compiler support to enforce cross-queue boundaries right now, so there is the potential to leak memory (which is OK for infrequent panics) or corrupt memory (unacceptable).

My guess is we could do something like this and in fact will need to do something like this, but only when we have a good concurrency story enforced by the compiler.

Russ

···

On Jan 12, 2017, at 2:58 PM, Jonathan Hull via swift-evolution <swift-evolution@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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #7

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 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++.

···

On Jan 12, 2017, at 3:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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


(Xiaodi Wu) #8

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@gmail.com> wrote:

On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < > swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #9

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.

Sure, that's not the meat of why I'm uncomfortable with the idea. I am
saying, however, that you've outlined as motivation two use cases here that
your proposal does not make possible.

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!’.

Have a gander at this document:
https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst

In particular, have a look at the section titled "Kinds of error." I think
what you're describing here is that you've got something that's neither a
simple domain error nor a recoverable error, as classically described. And
you'll see that there are some kinds of error for which Swift doesn't
currently have a good answer. I'd be interested in some real-world use
cases, though, and a more holistic approach to these other kinds of error
would be called for. It's why I'm saying you've got the beginning of an
interesting idea, but it's a lot more involved than you're making it out to
be.

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 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.

My arguments haven't been expressed very clearly, then, if you think that
they support such an interpretation. Instead, `as!` and `!` are analogous
to `try!`: they are used at the call site to statically assert something.
By contrast, your proposal is essentially to have these inferred silently,
as a default supplied at the declaration site. There is a distinction
between these two, and as pointed out in the other conversation referenced
above, Swift is said to choose call site clarity over defaults as a general
overarching principle.

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.

It is not, as far as I'm aware, the plan of record in any sense; it has not
been proposed and is not an identified priority of this phase of Swift 4
for proposal.

If we consider each feature only in isolation then we will end up with a

···

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

On Jan 12, 2017, at 3:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #10

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

···

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

On Jan 12, 2017, at 3:35 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #11

That works for single-expression closures, but as soon as the closure becomes more complex - say, if you want to transform the result of somethingThatMightFail() before returning it - it becomes valuable to see the point at which the entire operation might fail

···

On 13 Jan 2017, at 17:10, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 12, 2017, at 2:58 PM, Jonathan Hull via swift-evolution <swift-evolution@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.

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. 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.

I think this would really increase the availability of error handling in areas where it is impractical right now…

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Another place I can see this being useful is for higher-order functions which "obviously" work on a throwing operation. The most obvious example would be a constructor for a Result type that collects the success or error result from a throwing closure:

enum Result<T> {
case ok(T)
case error(Error)

init(_ f: () throws -> T) {
   do {
     self = .ok(try f())
   } catch {
     self = .error(error)
   }
}
}

Having to write `Result { try somethingThatMightFail() }` feels a bit silly because "obviously" the operation you pass to Result is going to be able to fail. Such a feature does feel like it could be easy to misuse, though.

-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #12

Just wanted to chime in on performance, but since operators should really be inlined in most cases anyway I'm not sure there should be any performance penalty; the compiler should just optimise it away such that it basically becomes what it is now, the only case in which it would add a penalty is if the optional try is actually used.

I really like this idea personally; I prefer it to using the addWithOverflow() and similar methods, not that they're super burdensome, but error handling feels like a better fit IMO.

···

On 12 Jan 2017, at 23:35, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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.


(Callionica (Swift)) #13

One stray thread performing an overflowing addition can be the difference
between a secure system and an insecure one. Better to take the process
down.

-- Callionica

···

On Sun, Jan 15, 2017 at 10:48 PM Russ Bishop via swift-evolution < swift-evolution@swift.org> wrote:

I don’t think it makes sense to abort a server process (potentially
dropping X threads and thousands of connections on the ground) because one
stray thread performed an overflowing addition…


(Kevin Nattinger) #14

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:

Java (functionally) has two types of exceptions, checked and unchecked.
Checked exceptions must either be caught and handled or the calling method must be declared as throwing the same exception (or an ancestor).
Unchecked exceptions do not need to be declared, and can either be caught or implicitly work their way back up the stack until either they’re caught or they hit the top of the stack (in which case the JRE kills the program.

Objective-C’s exception system is essentially Java’s unchecked type, and Swift’s is checked (with the addition of requiring `try` at the call site. At risk of getting off on a tangent, I really dislike the raw `try` requirement. `try?` and `try!` at least make sense as they specify how to handle the exception; `try` does not.)
Swift’s exception system is basically checked exceptions; Objective-C’s is unchecked.

As far as I can tell, this proposal isn’t really like either; `throws?` would swallow the exception entirely if the caller didn’t handle it and `throws!` would kill the app without giving higher stack frames a chance to catch it. I’m really not a fan of either; I’d much prefer unchecked exceptions, and I think they fulfill the OP’s underlying concerns.

···

On Jan 12, 2017, at 3:50 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

"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@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #15

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’.

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’).

Thanks,
Jon

···

On Jan 12, 2017, at 3:50 PM, Xiaodi Wu <xiaodi.wu@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@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #16

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,

···

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

Jon

On Jan 12, 2017, at 3:50 PM, Xiaodi Wu <xiaodi.wu@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@gmail.com> wrote:

On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < >> swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #17

`addWithOverflow()` gives you both the result and a flag; error handling
would at most give you one of these two. If that's a common use case, it'd
be an argument for a failable addition operator, but not a throwing one,
since there is only one error that happens (overflow).

···

On Sun, Jan 15, 2017 at 3:12 PM, Haravikk <swift-evolution@haravikk.me> wrote:

On 12 Jan 2017, at 23:35, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

On Thu, Jan 12, 2017 at 4:58 PM, Jonathan Hull via swift-evolution < > swift-evolution@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.

Just wanted to chime in on performance, but since operators should really
be inlined in most cases anyway I'm not sure there should be any
performance penalty; the compiler should just optimise it away such that it
basically becomes what it is now, the only case in which it would add a
penalty is if the optional try is actually used.

I really like this idea personally; I prefer it to using the
addWithOverflow() and similar methods, not that they're super burdensome,
but error handling feels like a better fit IMO.


(David Waite) #18

My interpretation is that he was advocating a future where a precondition’s failure killed less than the entire process. Instead, shut down some smaller portion like a thread, actor, or container like .Net's app domains (which for those more familiar with Javascript could be loosely compared with Web Workers).

Today - if you wanted a Swift server where overflowing addition didn’t interrupt your service for multiple users, you would need to use something like a pre-fork model (with each request handled by a separate swift process)

That's the difference between CLI and desktop apps where the process is providing services for a single user, and a server where it may be providing a service for thousands or millions of users.

-DW

···

On Jan 16, 2017, at 2:32 PM, Callionica (Swift) via swift-evolution <swift-evolution@swift.org> wrote:

One stray thread performing an overflowing addition can be the difference between a secure system and an insecure one. Better to take the process down.

-- Callionica

On Sun, Jan 15, 2017 at 10:48 PM Russ Bishop via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don’t think it makes sense to abort a server process (potentially dropping X threads and thousands of connections on the ground) because one stray thread performed an overflowing addition…

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #19

Agreed, I’d also really like to see this some day. It seems like a natural outgrowth of the concurrency model, if it goes the direction of actors. If you’re interested, I speculated on this direction in this talk:

-Chris

···

On Jan 16, 2017, at 3:57 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

My interpretation is that he was advocating a future where a precondition’s failure killed less than the entire process. Instead, shut down some smaller portion like a thread, actor, or container like .Net's app domains (which for those more familiar with Javascript could be loosely compared with Web Workers).

Today - if you wanted a Swift server where overflowing addition didn’t interrupt your service for multiple users, you would need to use something like a pre-fork model (with each request handled by a separate swift process)

That's the difference between CLI and desktop apps where the process is providing services for a single user, and a server where it may be providing a service for thousands or millions of users.


(Kenny Leung) #20

It would also enable the testing of fatal conditions, which would be great.

-Kenny

···

On Jan 16, 2017, at 5:25 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 16, 2017, at 3:57 PM, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My interpretation is that he was advocating a future where a precondition’s failure killed less than the entire process. Instead, shut down some smaller portion like a thread, actor, or container like .Net's app domains (which for those more familiar with Javascript could be loosely compared with Web Workers).

Today - if you wanted a Swift server where overflowing addition didn’t interrupt your service for multiple users, you would need to use something like a pre-fork model (with each request handled by a separate swift process)

That's the difference between CLI and desktop apps where the process is providing services for a single user, and a server where it may be providing a service for thousands or millions of users.

Agreed, I’d also really like to see this some day. It seems like a natural outgrowth of the concurrency model, if it goes the direction of actors. If you’re interested, I speculated on this direction in this talk:
http://researcher.watson.ibm.com/researcher/files/us-lmandel/lattner.pdf

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution