Type-annotated throws


(Félix Cloutier) #1

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.

Félix


Why doesn't Swift have explicit throwables like Java
(Anton Zhilin) #2

Felix, this idea has already been discussed, and Chris Lattner specifically
mentioned that it is planned for Swift 4 Phase 2. But right now we
shouldn't create a proposal for this.


(Rod Brown) #3

(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited API - you know it’s throwing out something, but you can only hope to guess what that is or create fallbacks. Definitely a big +1 from me. A fallback for compatibility could be “throws” assumes “throws Any” and can be a warning?

While I am not deeply familiar with the implications, I do like think your line of reasoning has merit, and think this makes sense for Phase 1 of Swift 4.

- Rod

···

On 27 Aug 2016, at 1:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.

Félix

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


#4

Hi,

Shouldn't the compiler be able to infer the types thrown, and thus whether all types have been thrown? The general idea being that the following would be valid:

enum Foo: Error {
    case bar
    case baz
}

func hello() throws { // inferred to throw only "Foo.bar"
    throw Foo.bar
}

func world() throws { // inferred to throw only "Foo.baz"
    throw Foo.baz
}

func galaxy() throws { // inferred to throw only "Foo.baz"
    do {
        try hello()
    } catch Foo.bar {
        // ...
    }
    // catch is exhaustive, no catch-all clause needed

    try world()
}

func universe() { // all errors are handled, no 'throws' declaration needed
    do {
        try galaxy()
    } catch Foo.baz {
        // ..
    }
    // catch is exhaustive, no catch-all clause needed
}

Now for clarity one could add the type information as per your proposal, but wouldn't be necessary as the compiler would infer it itself.

Either way a +1 from me, as the current model forces one to catch-all errors, even the ones you did not expect to be thrown. Thus potentially hiding programming errors.

- Bouke

···

On 2016-08-26 15:39:05 +0000, F�lix Cloutier via swift-evolution said:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
� � case bar
� � case baz
}

func frob() throws Foo {
� � throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.
F�lix

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


(Joe Groff) #5

Typed throws is largely additive, since an API that `throws` today can be compatibly changed to `throws T` in the future, so this isn't an absolute ABI issue.

-Joe

···

On Aug 26, 2016, at 8:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.


(Tino) #6

I'm quite skeptical here (Java has already been mentioned…), but if it's done, I'd vote for removing ErrorType:
This empty protocol always felt a little bit odd to me, and when each function has information about what errors to expect, imho there is absolutely no benefit associated with conformance.
Even now, it's not that much — or does anyone actually consult a list of conforming types for error handling?

Tino


(Russ Bishop) #7

This contract is a lie in almost all real-world programs because real programs talk to the network, the filesystem, the database, etc and the class of errors that can be thrown is tremendous. The number of functions where “throws Foo” is an improvement and Foo is not an exhaustive list of “throws HTTPError, NetworkError, StorageError, DatabaseError, MachPortError, …” is close enough to zero to be considered zero. This ultimately leads to library authors merely wrapping exceptions using generic catch-all blocks anyway, adding no useful information.

There is zero value to adding it without the compiler enforcing it because you can only omit default catch blocks safely when you can be certain the list of exceptions is exhaustive.

I am sympathetic to the default/generic catch block problem but we could certainly solve that with some added syntax or automatic insertion of a rethrow if an error doesn’t match, meaning any function with a try{} and no default catch must itself be a throwing function.

Since this isn’t applicable to Swift 4 phase 1 I’ll hush now :slight_smile:

Russ

···

On Aug 26, 2016, at 8:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.


(Kevin Nattinger) #8

+1 from me. I’m usually highly defensive about types, so the more the compiler can guarantee the better.

Hi,

Sounds like checked exceptions of Java with a similar syntax. With Java you have to specify the exceptions a method can throw. However been lots of debate over the years whether it's a good thing or bad thing, some like it, but I think many more hate it.

I’m pretty sure what people don’t like in Java is the enforced error handling or propagation and how much you have to nest to, e.g. set up IO, though I believe that has been mitigated somewhat by try-with-resources), not the specificity (and how it’s not obvious to many which throws need to be caught at compile time and which are RuntimeExceptions). Though I don’t write much Java nowadays, I know I never liked all the boilerplate that required.

Most other languages don't have it, and possibly for good reason.

I’d love to hear those arguments against letting the compiler make more guarantees.

Regards,

(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited API - you know it’s throwing out something, but you can only hope to guess what that is or create fallbacks. Definitely a big +1 from me. A fallback for compatibility could be “throws” assumes “throws Any” and can be a warning?

I’d suggest a bare `throws` = `throws ErrorType` (you can only throw ErrorType, right?) and be valid, I don’t see any reason to make it a warning (plus that keeps source compatibility).

···

On Aug 26, 2016, at 9:01 AM, Nur Ismail via swift-evolution <swift-evolution@swift.org> wrote:
On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While I am not deeply familiar with the implications, I do like think your line of reasoning has merit, and think this makes sense for Phase 1 of Swift 4.

- Rod

On 27 Aug 2016, at 1:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.

Félix

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


(David Waite) #9

I believe the issue with checked exceptions in Java are multi-fold.

First, there are both checked and unchecked exceptions, which gives developers a way to avoid checked exceptions completely. RuntimeException is unchecked and meant to handle runtime detectable issues like math overflow or arrays indexed out of bounds, which would be too unwieldy for developers to have to catch explicitly - especially when they may “know” that an expression like "1 + 1" will not overflow. In Swift, many of these issues actually result in a fatal error.

RuntimeExceptions are also used extensively to work around cases where the appropriate checked exception is not defined on an interface or superclass. In these cases you cannot expand the list of checked exceptions declared, but you still want to throw on error. In this case, you wrap your desired exception in a RuntimeException, or just throw your hands up in the air and throw a RuntimeException with a string description. With such a large escape hatch from checked exceptions, the verbosity of doing things “the correct way” becomes a much more obvious burden.

Java does not have pattern matching in exceptions, with built-in support only for switching on class type of caught exception. However, many modules outside the very core of the language declare a single module-level exception, e.g. AWTException. This means there is an impedance mismatch - the declaration of exception types does not map well always to the appropriate recovery mechanism from the exception.

Having checked exceptions makes it more likely that interfaces will not be declared throwing, as the interface creator may not know the appropriate types of exceptions to be declared.

An interface expected to return an object might get the object from the network, filesystem,. or a database - resulting in three different families of possible exception types. This is usually worked around by having a module-level exception that is declared as the checked type, which can hold the actual exception which happened. Because the module-level exceptions do not differentiate classifications of issues, and the implementation of the interface does not have the actual kind of exception as part of the interface contract, you are limited in your ability to handle the exception in your code. It becomes commonplace to have a single “that thing you tried to do failed”, with a long stack trace to enable a developer or someone in a supporting role to diagnose which code was involved in the failure.

This gets to the root of the problem - since the focus is on documenting the class types of exceptions rather than categories of error states, the developer doesn’t feel any of the complexity or verbosity helps them make their code more robust. Instead of considering how to represent errors to your own callers for recovery, it becomes common to just declare the same list of checked exceptions and pass them through upstream unhanded - or to wrap them all in a module-level exception type to simplify the interface. The end result is that the exception mechanism is made more heavyweight via the checked mechanism, but still doesn’t enable developers to make their code more robust.

In the end, I believe the thing to emphasize is that developers of an API need to think of how the errors they raise would be handled by potential callers, and to represent *that* in their API. This might result in a specific error enumeration per method, which is O.K. In that case, declaring such a thrown error so that the error cases may be handled exhaustively is probably a good thing. A declaration which either does not allow exhaustive handling of error cases, or which allows someone to forward errors from multiple sources without thinking about their caller’s error needs are probably not productive.

-DW

···

On Aug 26, 2016, at 10:01 AM, Nur Ismail via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Sounds like checked exceptions of Java with a similar syntax. With Java you have to specify the exceptions a method can throw. However been lots of debate over the years whether it's a good thing or bad thing, some like it, but I think many more hate it.
Most other languages don't have it, and possibly for good reason.

Regards,

On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited API - you know it’s throwing out something, but you can only hope to guess what that is or create fallbacks. Definitely a big +1 from me. A fallback for compatibility could be “throws” assumes “throws Any” and can be a warning?

While I am not deeply familiar with the implications, I do like think your line of reasoning has merit, and think this makes sense for Phase 1 of Swift 4.

- Rod

On 27 Aug 2016, at 1:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.

Félix

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


(Haravikk) #10

I like checked exceptions in Java so I'd be in support of this, as I feel it's a helpful feature to ensure that exceptions are properly handled.

I never really understood the complaints against it, as you're still free to just use a generic catch block for "everything else", and we have things like try? and try! when you don't expect an exception and just want to keep things simple, so the situation is already easier than in Java.

I'd say that there should still be generic "could throw anything" variant, like throws ErrorProtocol or such, but in most cases a well-defined error type (or several types?) is best, as developers should really think about what types of errors they produce and how someone might handle them when designing a method, and users of the method should be thinking about how they can recover from an error if it occurs, or fail in a more graceful/informative manner if they can't. Currently I don't think Swift's untyped throws achieve that.

···

On 26 Aug 2016, at 17:01, Nur Ismail via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Sounds like checked exceptions of Java with a similar syntax. With Java you have to specify the exceptions a method can throw. However been lots of debate over the years whether it's a good thing or bad thing, some like it, but I think many more hate it.
Most other languages don't have it, and possibly for good reason.

Regards,

On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited API - you know it’s throwing out something, but you can only hope to guess what that is or create fallbacks. Definitely a big +1 from me. A fallback for compatibility could be “throws” assumes “throws Any” and can be a warning?

While I am not deeply familiar with the implications, I do like think your line of reasoning has merit, and think this makes sense for Phase 1 of Swift 4.

- Rod

On 27 Aug 2016, at 1:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.

Félix

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


(Nur Ismail) #11

Hi,

Sounds like checked exceptions of Java with a similar syntax. With Java you
have to specify the exceptions a method can throw. However been lots of
debate over the years whether it's a good thing or bad thing, some like it,
but I think many more hate it.
Most other languages don't have it, and possibly for good reason.

Regards,

···

On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution < swift-evolution@swift.org> wrote:

(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited
API - you know it’s throwing out something, but you can only hope to guess
what that is or create fallbacks. Definitely a big +1 from me. A fallback
for compatibility could be “throws” assumes “throws Any” and can be a
warning?

While I am not deeply familiar with the implications, I do like think your
line of reasoning has merit, and think this makes sense for Phase 1 of
Swift 4.

- Rod

On 27 Aug 2016, at 1:39 AM, Félix Cloutier via swift-evolution < > swift-evolution@swift.org> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was
a proposal draft last December to restrict that. The general idea was that
you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can
verify that your catch is exhaustive, you no longer have to have a
catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which
leads me to believe that the discussion could qualify for the phase 1 of
Swift 4. If this is the case, I'd be interested in pulling out the old
discussions and seeing where we left that at.

Félix

_______________________________________________
swift-evolution mailing list
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


(Rod Brown) #12

(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited API - you know it’s throwing out something, but you can only hope to guess what that is or create fallbacks. Definitely a big +1 from me. A fallback for compatibility could be “throws” assumes “throws Any” and can be a warning?

I’d suggest a bare `throws` = `throws ErrorType` (you can only throw ErrorType, right?) and be valid, I don’t see any reason to make it a warning (plus that keeps source compatibility).

*hits head on desk*

How could I miss that? `throws` = `throws ErrorType` makes perfect sense. It also means we can get some type safety, but it’s not lock in.

Thanks :slight_smile:

-Rod

···

On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Adrian Zubarev) #13

@Anton: We still allowed to discuss it now. :slight_smile: That won’t bother people that much.

Just a side note without reading all the stuff going on here.

As much as I would love to have some type safety error handling, I do believe it should be optional in first place.

Image some scenario like this:

func foo() throws AError {}
func boo() throws BError {}
func zoo() throws CError {}

// We also need some design to handle all errors at once:
func handle() rethrows AError & BError & CError -> ReturnType {}

// As you can imagine the line might become really long and ugly looking
// Merging with existential syntax or typealias?
func handle() rethrows Any<AError, BError, CError> -> ReturnType {}

typealias ABCError = AError & BError & CError

func handle() rethrows ABCError -> ReturnType {}

// But sometime you don't really care which error type might be thrown and your API handles it with some other convention (docs?)
func handle() rethrows -> ReturnType {
    try foo()
    try boo()
    try zoo()
    return ReturnType()
}
A mix of a type safe and the current error handling mechanism would be great, but not just the type safe (except we’ll get AnyError, where just re-/throws might be a shorthand form for re-/throws AnyError).


(Félix Cloutier) #14

It would be a breaking change since it modifies the behavior of `throws` without a type parameter. It would also be incompatible with the declaration of methods in protocols.

Félix

···

Le 28 août 2016 à 01:44:27, Bouke Haarsma via swift-evolution <swift-evolution@swift.org> a écrit :

On 2016-08-26 15:39:05 +0000, Félix Cloutier via swift-evolution said:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.
Félix

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

Hi,

Shouldn't the compiler be able to infer the types thrown, and thus whether all types have been thrown? The general idea being that the following would be valid:

enum Foo: Error {
    case bar
    case baz
}

func hello() throws { // inferred to throw only "Foo.bar"
    throw Foo.bar
}

func world() throws { // inferred to throw only "Foo.baz"
    throw Foo.baz
}

func galaxy() throws { // inferred to throw only "Foo.baz"
    do {
        try hello()
    } catch Foo.bar {
        // ...
    }
    // catch is exhaustive, no catch-all clause needed

    try world()
}

func universe() { // all errors are handled, no 'throws' declaration needed
    do {
        try galaxy()
    } catch Foo.baz {
        // ..
    }
    // catch is exhaustive, no catch-all clause needed
}

Now for clarity one could add the type information as per your proposal, but wouldn't be necessary as the compiler would infer it itself.

Either way a +1 from me, as the current model forces one to catch-all errors, even the ones you did not expect to be thrown. Thus potentially hiding programming errors.

- Bouke
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #15

I would definitely give a strong -1 to any proposal that tried to have the compiler infer this, because it would break the ability for a future revision of galaxy() to add a new error that could potentially be thrown. If we’re going to add typed throws, they should only be added if deliberately specified by the developer.

Charles

···

On Aug 28, 2016, at 3:44 AM, Bouke Haarsma via swift-evolution <swift-evolution@swift.org> wrote:

On 2016-08-26 15:39:05 +0000, Félix Cloutier via swift-evolution said:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
    case bar
    case baz
}

func frob() throws Foo {
    throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This impacts the metadata format and has implications on resilience, which leads me to believe that the discussion could qualify for the phase 1 of Swift 4. If this is the case, I'd be interested in pulling out the old discussions and seeing where we left that at.
Félix

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

Hi,

Shouldn't the compiler be able to infer the types thrown, and thus whether all types have been thrown? The general idea being that the following would be valid:

enum Foo: Error {
    case bar
    case baz
}

func hello() throws { // inferred to throw only "Foo.bar"
    throw Foo.bar
}

func world() throws { // inferred to throw only "Foo.baz"
    throw Foo.baz
}

func galaxy() throws { // inferred to throw only "Foo.baz"
    do {
        try hello()
    } catch Foo.bar {
        // ...
    }
    // catch is exhaustive, no catch-all clause needed

    try world()
}

func universe() { // all errors are handled, no 'throws' declaration needed
    do {
        try galaxy()
    } catch Foo.baz {
        // ..
    }
    // catch is exhaustive, no catch-all clause needed
}

Now for clarity one could add the type information as per your proposal, but wouldn't be necessary as the compiler would infer it itself.

Either way a +1 from me, as the current model forces one to catch-all errors, even the ones you did not expect to be thrown. Thus potentially hiding programming errors.


(Charles Srstka) #16

Error is far from empty; it appears that way, but it contains default implementations to make bridging to and from Cocoa NSError objects much more convenient, especially since SE-0112. Plus, it’s a protocol rather than a base class, so conforming it doesn’t really constrain your design in any way.

-1 on removing Error.

Charles

···

On Aug 29, 2016, at 4:18 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

I'm quite skeptical here (Java has already been mentioned…), but if it's done, I'd vote for removing ErrorType:
This empty protocol always felt a little bit odd to me, and when each function has information about what errors to expect, imho there is absolutely no benefit associated with conformance.
Even now, it's not that much — or does anyone actually consult a list of conforming types for error handling?

Tino


(Anton Zhilin) #17

+1 for removing Error protocol. Then it's a breaking change, and this
edition of the proposal can be reviewed for Stage 1.
Swift error model tries to be different (better) than of other languages.
We encourage not to rethrow, but to think of errors as of meaningful
results and process them accordingly, at all levels of abstraction.
Right now, the formal proposal can't be submitted, but discussion is fine.

···

2016-08-29 12:18 GMT+03:00 Tino Heth via swift-evolution < swift-evolution@swift.org>:

I'm quite skeptical here (Java has already been mentioned…), but if it's
done, I'd vote for removing ErrorType:
This empty protocol always felt a little bit odd to me, and when each
function has information about what errors to expect, imho there is
absolutely no benefit associated with conformance.
Even now, it's not that much — or does anyone actually consult a list of
conforming types for error handling?


(Félix Cloutier) #18

I see two main use cases for throws right now:

for high-level operations that can fail for a lot of reasons
when it's convenient to bubble out of a complex, nested/recursive algorithm (like parsing a data structure)

Throws works well for case one, but it's not awesome for case two. I don't think that adding an optional type annotation to throws would hurt case one a lot, too.

Félix

···

Le 29 août 2016 à 21:13:51, Russ Bishop <xenadu@gmail.com> a écrit :

On Aug 26, 2016, at 8:39 AM, Félix Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Currently, a function that throws is assumed to throw anything. There was a proposal draft last December to restrict that. The general idea was that you'd write, for instance:

enum Foo: ErrorProtocol {
   case bar
   case baz
}

func frob() throws Foo {
   throw Foo.bar // throw .bar?
}

If you `catch Foo` (or every case of Foo), now that the compiler can verify that your catch is exhaustive, you no longer have to have a catch-all block at the end of the sequence.

This contract is a lie in almost all real-world programs because real programs talk to the network, the filesystem, the database, etc and the class of errors that can be thrown is tremendous. The number of functions where “throws Foo” is an improvement and Foo is not an exhaustive list of “throws HTTPError, NetworkError, StorageError, DatabaseError, MachPortError, …” is close enough to zero to be considered zero. This ultimately leads to library authors merely wrapping exceptions using generic catch-all blocks anyway, adding no useful information.

There is zero value to adding it without the compiler enforcing it because you can only omit default catch blocks safely when you can be certain the list of exceptions is exhaustive.

I am sympathetic to the default/generic catch block problem but we could certainly solve that with some added syntax or automatic insertion of a rethrow if an error doesn’t match, meaning any function with a try{} and no default catch must itself be a throwing function.

Since this isn’t applicable to Swift 4 phase 1 I’ll hush now :slight_smile:

Russ


(Xiaodi Wu) #19

I think the core team has previously mentioned some positive notions about
typed throws, so it'd be interesting to hear what they have in mind at this
point (or, more likely, at the point when they're no longer swamped with
getting Swift 3 out the door).

···

On Fri, Aug 26, 2016 at 11:55 AM, Rod Brown via swift-evolution < swift-evolution@swift.org> wrote:

On Fri, Aug 26, 2016 at 5:43 PM, Rod Brown via swift-evolution <swift- > evolution@swift.org> wrote:

(resent for Swift Evolution)

I’m a big fan of this idea. Currently “throws” seems like a very limited
API - you know it’s throwing out something, but you can only hope to guess
what that is or create fallbacks. Definitely a big +1 from me. A fallback
for compatibility could be “throws” assumes “throws Any” and can be a
warning?

I’d suggest a bare `throws` = `throws ErrorType` (you can only throw
ErrorType, right?) and be valid, I don’t see any reason to make it a
warning (plus that keeps source compatibility).

*hits head on desk*

How could I miss that? `throws` = `throws ErrorType` makes perfect sense.
It also means we can get some type safety, but it’s not lock in.

Thanks :slight_smile:

-Rod

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


(Anton Zhilin) #20

Forwarding to more people:
This idea has already been discussed, and Chris Lattner specifically
mentioned that this proposal will be suitable for Swift 4 Phase 2. But
right now we shouldn't create a proposal for this.