# SE-0413: Typed throws

**URL:** <https://forums.swift.org/t/se-0413-typed-throws/68507>\
**Category:** Proposal Reviews\
**Created:** [November 17, 2023, 2:20am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507 "2023-11-17T02:20:48Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [November 17, 2023, 2:20am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/1 "2023-11-17T02:20:48Z")

</div>

Hello, Swift community!

The review of [SE-0413: Typed throws](https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md) begins now and runs through Thursday, December 7th, 2023.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager by [email](mailto:scanon@apple.com) or [DM](https://forums.swift.org/u/scanon/summary). When contacting the review manager directly, please put "SE-0413" in the subject line.

##### Trying it out

If you'd like to try this proposal out, you can download a toolchain supporting it:

- [macOS](https://ci.swift.org/job/swift-PR-toolchain-macos/946/artifact/branch-main/swift-PR-69771-946-osx.tar.gz)
- [Linux](https://download.swift.org/tmp/pull-request/69771/644/ubuntu2004/PR-ubuntu2004.tar.gz)
- [Windows](https://ci-external.swift.org/job/swift-PR-build-toolchain-windows/1021/artifact/*zip*/archive.zip)

You will need to add `-enable-experimental-feature TypedThrows ` to your build flags to use the feature.

These toolchains do not implement thrown type inference for closures, so if you want a closure to have a specific thrown error type, you need to write it out as, e.g., `{ () throws(MyError) in … }`

##### What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

- What is your evaluation of the proposal?
- Is the problem being addressed significant enough to warrant a change to Swift?
- Does this proposal fit well with the feel and direction of Swift?
- If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
- How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:

> [https://github.com/apple/swift-evolution/blob/main/process.md](https://github.com/apple/swift-evolution/blob/main/process.md)

Thank you,

Steve Canon  
Review Manager

---

<div class="post-metadata">

**Author:** ![jordanekay](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jordanekay/32/2499_2.png) [@jordanekay](https://forums.swift.org/u/jordanekay)\
**Post date:** [November 17, 2023, 3:13am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/2 "2023-11-17T03:13:05Z")

</div>

The rationale for needing parentheses does not seem to hold up. The first example given is impossible (`(E) -> I` cannot confirm to `Error`) and the second example is purely speculative, invoking a feature that not only doesn’t exist but isn’t currently planned to.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 17, 2023, 3:45am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/3 "2023-11-17T03:45:22Z")

</div>

> [@jordanekay](#):
>
> The first example given is impossible (`(E) -> I` cannot confirm to `Error`)

Whether `(E) -> I` conforms to `Error` or not isn't up to the parser. As the proposal states, baking into the language grammar the status quo that functions can't conform to any protocols—even by magic—is certainly possible, but it complicates the grammar and has the disadvantage of, well, baking in this status quo.

> [@jordanekay](#):
>
> the second example is purely speculative, invoking a feature that not only doesn’t exist but isn’t currently planned to.

The second example isn't exactly speculative: We already have another effect called `async`, but if you want to make life painful for yourself and/or your users, Swift allows you to declare `struct async: Error { }`.

Currently, we disallow (arbitrarily) `func f() throws async` in favor of `async throws`. However, if typed throws did not require parentheses, then `func f() throws async` would become valid Swift, and it would mean a synchronous function that throws an error of type `async`. This would be the case _whether or not you declare such an unhinged type_, because it's not up to the grammar what types you declare or not so long as it's a valid identifier.

(We could, of course, say that all effect names must start with ASCII lowercase and that `Error`-conforming types must _not_ start with ASCII lowercase—but that is technically source-breaking and, again, complicates the grammar.)

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 17, 2023, 3:52am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/4 "2023-11-17T03:52:33Z")

</div>

> [@xwu](#):
>
> Currently, we disallow (arbitrarily) `func f() throws async` in favor of `async throws`. However, if typed throws did not require parentheses, then `func f() throws async` would become valid Swift, and it would mean a synchronous function that throws an error of type `async`. This would be the case _whether or not you declare such an unhinged type_, because it's not up to the grammar what types you declare or not so long as it's a valid identifier.

Admittedly, the compiler _could_ detect this and essentially just ban it, by throwing an error a la "Cannot determine if you meant to make this function async or throw the Error type `async` (you should probably rename your `async` struct)".

But there are other potential benefits of the parenthesis, like readability through compartmentalisation. e.g. for `throws(some NetworkError)`, let-alone for less trivial types like `throws(some NetworkError & LoggableError & ChainableError)`.

---

<div class="post-metadata">

**Author:** ![woolsweater](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/woolsweater/32/2402_2.png) [@woolsweater](https://forums.swift.org/u/woolsweater)\
**Post date:** [November 17, 2023, 4:05am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/5 "2023-11-17T04:05:57Z")

</div>

Somewhat minor point, but I didn't see this explicitly mentioned. A throwing protocol property requirement will also be able to specify a type? I.e.

```swift
protocol Foo {
  var bar: Bar { get throws(BarError) }
}

```

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 17, 2023, 4:54am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/6 "2023-11-17T04:54:35Z")

</div>

> When a function signature uses a generic parameter or associated type as a thrown type, that generic parameter or associated type is implicitly inferred to conform to the `Error` type. For example, given this declaration for `map`:
> 
> `func map<T, E>(body: (Element) throws(E) -> T) throws(E) { ... }`
> 
> the function has an inferred requirement `E: Error`.

Is that for some subtle functional reason, or purely for convenience?

If the latter, I'm not sure it's wise (genuinely - maybe it's fine, but something about it makes me pause). The thrown type parameter can appear in other places in the function declaration. It could be possible for someone to fixate on the parameters, glossing over the `throws` declaration hanging off the end, and not realise that there's an inferred `Error` conformance constraint on the type parameter.

That said, this inference _already_ applies for e.g. `Result`:

```swift
func execute<E>() -> Result<Void, E> { … } // okay in Swift 5.9

```

…so I guess that ship has sailed, in essence. Maybe worth just explicitly noting this precedence, in the SEP?

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 17, 2023, 5:02am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/7 "2023-11-17T05:02:59Z")

</div>

> `Result`'s [init(catching:)](https://developer.apple.com/documentation/swift/result/3139399-init) operation translates a throwing closure into a `Result` instance. It's currently defined only when the `Failure`type is `any Error`, i.e.,
> 
> `init(catching body: () throws -> Success) where Failure == any Error { ... }`
> 
> Replace this with an initializer that uses typed throws:
> 
> `init(catching body: () throws(Failure) -> Success)`
> 
> The new initializer is more flexible: in addition to retaining the error type from typed throws, it also supports non-throwing closure arguments by inferring `Failure` to be equal to `Never`.

But is that a feature or a bug? As the proposal subsequently points out, that will let you write e.g.:

```swift
func randomNumber() -> Int {
    4 // https://xkcd.com/221
}

let result: Result<Int, Never> = .init(catching: randomNumber)

```

It seems potentially confusing, at least, to allow 'catching' from a closure / function argument that can't actually throw. The compiler doesn't allow similar cases like using the `try` keyword on a statement that doesn't throw (for good reason, IMO - it suggests a misunderstanding of something or mistake - e.g. wrote the wrong function name - by the code author).

It might select a non-throwing overload - the wrong overload, most likely, given the context - because technically that matches because it `throws(Never)`… unless the compiler is going to handle that specially?

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [November 17, 2023, 5:13am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/8 "2023-11-17T05:13:27Z")

</div>

You can already pass non-throwing closures to throwing-closure parameters, so this doesn’t make things any worse. It just makes the spelling more explicit.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 17, 2023, 5:34am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/9 "2023-11-17T05:34:38Z")

</div>

> Therefore, each `rethrows` operation in the standard library should be replaced with one that uses typed throws to propagate the same error type. For example, the `Optional.map` operation would change from:
> 
> `public func map<U>( _ transform: (Wrapped) throws -> U ) rethrows -> U?`
> 
> to
> 
> `public func map<U, E>( _ transform: (Wrapped) throws(E) -> U ) throws(E) -> U?`
> 
> This is a mechanical transformation that is applied throughout the standard library.

The pitch largely overlooks the key semantic of `rethrows`: that the function can only throw _if and when_ its arguments do, and that the compiler enforces this in the implementation of that function. It mentions it only partially and only at the _very_ end (and not even in the main proposal, but under 'Alternatives considered').

I'm not sure if it's intentional or not, but the pitch reads as having a pretty obvious subtext that it wishes `rethrows` were removed entirely. It _almost_ says exactly that at the very end.

If it is in fact the proposal's intent that `rethrows` ultimately be removed (even if not by this specific proposal), I suggest it just say that. That's not the sort of change that should be snuck in.

In any case, leaving `rethrows` dangling isn't great. With the proposal as it stands, `rethrows` is in a somewhat crippled state because as much as the proposal says `rethrows` can be almost entirely replaced, I don't think that's wise, and without support for typing, `rethrows` becomes a barrier to adoption of typed [re]throws.

Even _if_ it's essentially the same to _callers_ of rethrowing functions, I think the implementors of those functions still care for the `rethrows` behaviour re. enforcing intended logic. Or at least I do. I consider it a step towards explicitly pure functions; more generally, for removing unintended side-effects. In this case, accidental error situations that aren't actually caused by the closure arguments (as `rethrows` today means and [mostly] guarantees).

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 17, 2023, 5:37am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/10 "2023-11-17T05:37:47Z")

</div>

> [@jrose](#):
>
> You can already pass non-throwing closures to throwing-closure parameters, so this doesn’t make things any worse. It just makes the spelling more explicit.

Right, but I guess what I'm thinking is that this shouldn't be held up as _good_ behaviour, more just an existing oversight. And it's okay for the proposal to say "we're following the established conventions [even though they're not ideal, but that's a subject for a different proposal]".

Is there any workaround, with typed throws? e.g. can you replace `throws(Failure)` with something that means _actually_ throws something; any error type _but_ `Never`?

It's not a big deal, to be clear. I just think it's an unfortunate way to permit coding mistakes, that other parts of the language are already better at preventing (e.g. `try`, as noted).

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 17, 2023, 5:40am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/11 "2023-11-17T05:40:02Z")

</div>

> [@wadetregaskis](#):
>
> That said, this inference _already_ applies for e.g. `Result`:
> 
> ```swift
> func execute<E>() -> Result<Void, E> { … } // okay in Swift 5.9
> 
> ```
> 
> …so I guess that ship has sailed, in essence. Maybe worth just explicitly noting this precedence, in the SEP?

That ship sailed _long_ ago:

```swift
func f<K, V>() -> [K: V] { [:] }

struct S { }
f() as [S: S]
// Compiler error:
// Type 'S' does not conform to protocol 'Hashable'
// Global function 'f()' requires that 'S' conform to 'Hashable'

```

---

<div class="post-metadata">

**Author:** ![Zollerboy1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/zollerboy1/32/12997_2.png) [@Zollerboy1](https://forums.swift.org/u/Zollerboy1)\
**Post date:** [November 17, 2023, 8:14am UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/12 "2023-11-17T08:14:07Z")

</div>

> [@scanon](#):
>
> What is your evaluation of the proposal?

Overall, this looks fantastic and I'm very glad that this finally has the chance to become a part of the language.

I have one minor complaint though:

> ```swift
> do {
> try callCat() // throws CatError
> try callKids() // throw KidError
> } catch {
> // implicit 'error' variable has type 'any Error'
> }
> 
> ```
> 
> In essence, when there are multiple possible thrown error types, we immediately resolve to the untyped equivalent of `any Error` . We will refer to this notion as a type function `errorUnion(E1, E2, ..., EN)` , which takes `N` different error types (e.g., for throwing sites within a `do` block) and produces the union error type of those types. Our definition and use of `errorUnion` for typed throws subsumes the existing rule for untyped throws, in which every throw site produces an error of type `any Error` .

While I'm totally fine with the rule that multiple differently typed errors in a `do` block just produce an `any Error` in an unconstrained `catch`, I wonder why it isn't possible to exhaustively catch the errors like so:

```swift
do {
    try callCat() // throws CatError
    try callKids() // throws KidError
} catch is CatError {
    print("Error was CatError")
} catch is KidError {
    print("Error was KidError")
} // no catch-all needed because catch clauses are exhaustive

```

The proposal has the following note about this:

> **Note** : Exhaustiveness checking in the general is expensive at compile time, and the existing language uses the presence of an unconditional `catch` block as the indicator for an exhaustive `do...catch` . See the section on closure thrown type inference for more details about inferring throwing closures.

Can someone explain what's so expensive about exhaustiveness checking? In principle we would just have to loop over every error thrown in the `do` block and look if it is convertible to one of the `catch` clauses, no? I cannot imagine why this should be any harder than exhaustiveness checking for `switch` statements/expressions.

If there is a way to make `catch` clauses exhaustive without having to have a catch-all clause that would be fantastic.

* * *

> [@scanon](#):
>
> Is the problem being addressed significant enough to warrant a change to Swift?

Definitely. The lack of typed throws was always one of the major pain points for me when using Swift.

* * *

> [@scanon](#):
>
> Does this proposal fit well with the feel and direction of Swift?

IMHO, yes. The syntax feels intuitive, it will bring throws back to feature parity with `Result`/`Task` and will hopefully improve the standard library and especially the concurrency API in the long run.

* * *

> [@scanon](#):
>
> If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have used checked exceptions in Java before, but it feels like these are playing in a different league. I think, it's positive that the default in Swift will still be untyped errors, unlike Java where you either have exceptions that you don't have to declare at all, or the checked ones which have to be typed for every function that uses them. Swift seems to be striking a good balance there.

Honestly, the typed throws system in Swift feels very much like nice sugar around the error handling system in Rust, which I already really like.

* * *

> [@scanon](#):
>
> How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I read and participated in basically all discussion and pitch threads regarding this topic, carefully read this proposal as well as some of its earlier revisions, and even started to work with some others on an implementation for this feature a while ago (even though we didn't really get far due to lack of compiler knowledge and time).

---

<div class="post-metadata">

**Author:** ![tevelee](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tevelee/32/9775_2.png) [@tevelee](https://forums.swift.org/u/tevelee)\
**Post date:** [November 17, 2023, 12:32pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/13 "2023-11-17T12:32:24Z")

</div>

**What is your evaluation of the proposal?**

This is an excellent proposal, and I fully support it with a strong +1.

**Is the problem being addressed significant enough to warrant a change to Swift?**

Yes! It fills a major hole between the loss of information between types such as `Result` and `Task` and the Swift's error-handling system.

**Does this proposal fit well with the feel and direction of Swift?**

It provides a nice abstraction over carrying a generic type parameter throughout throwing effects, which eliminates the need for previous workarounds like rethrows and @rethrows protocol annotations. Additionally, it opens doors for AsyncSequence to FINALLY introduce primary associated types now that we have a notion to express a generic Failure type.

The only thing I miss from subtyping relationships (or from future directions) section is that  
we could accommodate subtyping the relationship of `f() -> Element?` and `f() throws(Void) -> Element`, as [brought up by Xiaodi in the pitch thread](https://forums.swift.org/t/pitch-n-1-typed-throws/67496/59).

**If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?**

Previously relying on Result types in Swift for internal error type information in our SDK was cumbersome, particularly during type conversion and unwrapping. Most other strongly types languages I used deal with similar Result constructs (e.g. Rust). What stands out in this proposal is the ability to consistently use multiple try statements within function bodies while preserving static error type information.

**How much effort did you put into your review? A glance, a quick reading, or an in-depth study?**

I closely monitored the proposal's evolution and its [discussion history](https://forums.swift.org/t/precise-error-typing-in-swift/52045), as well as Doug's pull requests throughout the process. Moreover, I had the opportunity to test it with the provided toolchain.

---

<div class="post-metadata">

**Author:** ![FranzBusch](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/franzbusch/32/19447_2.png) [@FranzBusch](https://forums.swift.org/u/FranzBusch)\
**Post date:** [November 17, 2023, 12:48pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/14 "2023-11-17T12:48:17Z")

</div>

**What is your evaluation of the proposal?**

I am a strong +1 on this proposal. Typed throws are going to provide a new tool to developers to express their APIs and I am glad we are finally filling this hole. The concern around adopting typed throws in public facing APIs is valid and the proposal rightly calls it out but in the end the authors of public APIs need to decide what language tools they want to use.

**If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?**

In my past, I have written large scale iOS applications that either implicitly constrained the thrown errors to a specific type and expected everything to wrap their errors in this type or used libraries such as `Combine` that already allowed concrete error typing.

**How much effort did you put into your review? A glance, a quick reading, or an in-depth study?**

I have been actively following the proposal and have been working with @ktoso and @Douglas_Gregor on a follow up proposal for adopting it in the `Concurrency` library. While writing this follow up proposal I realised that typed throws will actually make a lot of APIs easier to spell and remove a lot of duplication from the current implementations.

---

<div class="post-metadata">

**Author:** ![Nobody1707](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nobody1707/32/2169_2.png) [@Nobody1707](https://forums.swift.org/u/Nobody1707)\
**Post date:** [November 17, 2023, 3:26pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/15 "2023-11-17T15:26:57Z")

</div>

+1 This looks good to me.

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [November 17, 2023, 5:00pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/16 "2023-11-17T17:00:55Z")

</div>

A big +1. This is an excellent proposal. I appreciate its technical care. I appreciate its attention to language ergonomics. I appreciate its motivating examples and thoughtfully stated rationale. And I especially appreciate how carefully it translates that rationale into recommendations about ongoing best practice.

Some SE proposals are “Let’s see what people do with it!” proposals. Not this one. **Swift developer norms** — culture and habit — are going to be a crucial part of making this new feature play out well in practice, and it’s good that this proposal maps out what those norms should look like as a part of making its case. I’ve spent serious time living with Java's unhappy attempts to make error types explicit, and I do not relish a return to the world of `throw MyMeaninglessWrapperException(​exceptionThatIsMeaninglessToMe)`. But that unhappy world is a product of how the language gets used, not the feature itself. The proposal puts it beautifully:

> Errors are generally propagated and rendered, but rarely handled exhaustively, and are prone to changing over time in a way that types are not.

With that in mind, I appreciate and wholeheartedly agree with the **When to use typed throws** section. I hope those thoughts make it into both official Swift language documentation and general Swift developer parlance. Those guidelines answer my concerns about repeating the mistakes of the Java model.

As far as the language design substance of the proposal, everything seems entirely spot on. (The subtyping rules make me slightly nervous, but they’re clearly necessary and I can’t spot any problems.)

I particularly appreciate the way this addresses generality gaps in `rethrows`, a Swift language feature that’s always had a bad smell for me. This proposal fully addresses my concerns with it. (I wonder whether we might deprecate `rethrows` entirely in the future. Maybe. Let’s see how things play out.)

Nice work!

A few specific thoughts:

- I do not share the concerns expressed by others about the parentheses. The `throws(Oops)` syntax reads just fine to me. Even if it weren’t for the compiler parsing problems, in casual experiments, dropping the parens if anything makes _human_ parsing a bit harder to my eye — especially when the error type is not a simple type. The proposed syntax is good as is.

- This is probably going to cause large headaches for a small number of people, both on the Swift 5 and the Swift 6 sides of the wall:

- There’s some useful guidance in the proposal on when to use `some Error` versus `any Error`. Make sure that too ends up in the language guide.

- There was _extensive_ discussion during the pitch phase about introducing (untagged) sum types to Swift as a way of letting functions throw multiple types, as “Alternatives considered” briefly mentions. I’m skeptical of this direction, and happy that we are not proposing it at this time. (I’m similarly happy that we are not proposing multiple thrown types, which are just sum types in disguise, for exactly the reasons the proposal mentions.)

Again, great work on the proposal.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [November 17, 2023, 6:35pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/17 "2023-11-17T18:35:21Z")

</div>

> [@woolsweater](#):
>
> Somewhat minor point, but I didn't see this explicitly mentioned. A throwing protocol property requirement will also be able to specify a type? I.e.
> 
> ```swift
> protocol Foo {
> var bar: Bar { get throws(BarError) }
> }
> 
> ```

Yes.

> [@wadetregaskis](#):
>
> > When a function signature uses a generic parameter or associated type as a thrown type, that generic parameter or associated type is implicitly inferred to conform to the `Error` type. For example, given this declaration for `map`:
> > 
> > `func map<T, E>(body: (Element) throws(E) -> T) throws(E) { ... }`
> > 
> > the function has an inferred requirement `E: Error`.
> 
> Is that for some subtle functional reason, or purely for convenience?

It's a convenience, but one that we've always had when you use generic types within a function signature. As you noted, this is what implies the `Error` conformance for `E` in `Result<T, E>`, and its always what implies a `Hashable` conformance for `K` in `[K: V]`.

> [@wadetregaskis](#):
>
> It seems potentially confusing, at least, to allow 'catching' from a closure / function argument that can't actually throw. The compiler doesn't allow similar cases like using the `try` keyword on a statement that doesn't throw (for good reason, IMO - it suggests a misunderstanding of something or mistake - e.g. wrote the wrong function name - by the code author).

I think the uniform handling of throwing and non-throwing cases far outweighs the potential for confusion here. That's why we have `Never` in the type system, and why it conforms to `Error` in the first place.

> [@wadetregaskis](#):
>
> The pitch largely overlooks the key semantic of `rethrows`: that the function can only throw _if and when_ its arguments do, and that the compiler enforces this in the implementation of that function. It mentions it only partially and only at the _very_ end (and not even in the main proposal, but under 'Alternatives considered').

I agree that this is the key semantics of rethrows. With typed throws, this semantics mostly falls out from the definition of a generic function where the same type parameter is used for the throwing closure parameters and the resulting function. The `map` example:

```swift
public func map<U, E>( _ transform: (Wrapped) throws(E) -> U ) throws(E) -> U?

```

In the body of map, how are you going to throw an instance of type `E` if not by calling `transform`? Its only constraint is that it conforms to `Error`, and that protocol doesn't have any way to create a new instance. One would need to either add more constraints on `E` (which will show up in the signature) or do some kind of dynamic casting to create an instance of `E`.

> [@wadetregaskis](#):
>
> I'm not sure if it's intentional or not, but the pitch reads as having a pretty obvious subtext that it wishes `rethrows` were removed entirely. It _almost_ says exactly that at the very end.
> 
> If it is in fact the proposal's intent that `rethrows` ultimately be removed (even if not by this specific proposal), I suggest it just say that. That's not the sort of change that should be snuck in.

I suspect that `rethrows` will ultimately be removed from the language. `rethrows` is in this unfortunate place where there are [existing soundness holes](https://forums.swift.org/t/pitch-fix-rethrows-checking-and-add-rethrows-unsafe/44863) in the checking; yet these soundness holes are load-bearing because there are patterns you cannot write with `rethrows` (the proposal [shows this](https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md#an-alternative-to-rethrows)), and code in the wild is using these holes to provide good APIs. So `rethrows` either needs to be extended and made sound (e.g., the pitch I linked that proposes `rethrows(unsafe)`), or it needs to be removed.

This proposal isn't removing `rethrows` outright for several reasons:

1. It's a source-breaking change in a proposal that tries very hard not to be source-breaking.
2. It's possible that there uses of `rethrows` not well-covered by typed throws (although I have yet to see one).
3. The bar for removal of an existing feature is significantly higher than for the introduction of a new feature, and I don't want this proposal to be delayed by that higher bar.

It might be reasonable to _immediately_ follow up this proposal with another one that lays out the argument for eliminating `rethrows`. Personally, I think we can let it sit for a year or more to see whether typed throws has subsumed all uses of `rethrows`.

> [@Zollerboy1](#):
>
> The proposal has the following note about this:
> 
> > **Note** : Exhaustiveness checking in the general is expensive at compile time, and the existing language uses the presence of an unconditional `catch` block as the indicator for an exhaustive `do...catch` . See the section on closure thrown type inference for more details about inferring throwing closures.
> 
> Can someone explain what's so expensive about exhaustiveness checking? In principle we would just have to loop over every error thrown in the `do` block and look if it is convertible to one of the `catch` clauses, no? I cannot imagine why this should be any harder than exhaustiveness checking for `switch` statements/expressions.

It's trickier than that, because refutable patterns can also have arbitrary Boolean conditions in them. If I have series of `catch`es that each match the `HomeworkError` enum and check for one of the cases of that enum, and each case is checked, is that also exhaustive?

The actual issue is in closure type inference, though. A syntactically-exhaustive do..catch in a closure means that we know the closure isn't throwing. Doing semantic exhaustiveness checks like you describe means putting the complicated exhaustiveness algorithm (like the one used for `switch`) in the middle of type inference, meaning it may have to be run many times with different types as input---which can explode type-checking times. We avoid this problem with `switch` exhaustiveness checking because it only happens later in the compiler... and even then, we had to add timeouts for super-complex `switch` statements because it's effectively solving the satisfiability problem.

Doug

---

<div class="post-metadata">

**Author:** ![Slava\_Pestov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slava_pestov/32/30204_2.png) [@Slava\_Pestov](https://forums.swift.org/u/Slava_Pestov)\
**Post date:** [November 17, 2023, 6:46pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/18 "2023-11-17T18:46:28Z")

</div>

> [@Douglas\_Gregor](#):
>
> ```swift
> public func map<U, E>( _ transform: (Wrapped) throws(E) -> U ) throws(E) -> U?
> 
> ```
> 
> In the body of map, how are you going to throw an instance of type `E` if not by calling `transform`? Its only constraint is that it conforms to `Error`, and that protocol doesn't have any way to create a new instance. One would need to either add more constraints on `E` (which will show up in the signature) or do some kind of dynamic casting to create an instance of `E`.

And this here is a concise summary of why functional programming with parametric polymorphism is good—instead of reasoning about the dynamic execution of your program, you define your types so that the only possible way to inhabit them already satisfies your invariants.

Dynamic casting can fail, but as long as your function is total, you can only throw a thing you caught. This is expressed in the type signature, without the compiler having the reason about execution (which in the case of rethrows we know it does incorrectly).

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 17, 2023, 6:48pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/19 "2023-11-17T18:48:41Z")

</div>

> [@Douglas\_Gregor](#):
>
> It's trickier than that, because refutable patterns can also have arbitrary Boolean conditions in them. If I have series of `catch`es that each match the `HomeworkError` enum and check for one of the cases of that enum, and each case is checked, is that also exhaustive?

Would it be substantially more expensive to add the minimum of logic to handle the example given above? That is:

```swift
do {
  try callCat() // throws CatError
  try callKids() // throw KidError
} catch is CatError {
    print("Error was CatError")
} catch is KidError {
    print("Error was KidError")
}

```

I would imagine that from an end-user perspective the lack of heroic efforts to check exhaustivity is totally understandable, but the lack of any attempt to accommodate any more than one type might be rather more surprising.

Of course, I think it's also fair to point out that the end user's code can be trivially rewritten:

```swift
// ...
catch is CatError {
    print("Error was CatError")
} catch {
    // assert(error is KidError)
    print("Error was KidError")
}

```

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [November 17, 2023, 7:03pm UTC](https://forums.swift.org/t/se-0413-typed-throws/68507/20 "2023-11-17T19:03:04Z")

</div>

> [@xwu](#):
>
> Of course, I think it's also fair to point out that the end user's code can be trivially rewritten

Indeed, and I was picturing a different trivial rewrite:

```swift
do {
    try callCat() // throws CatError
} catch is CatError {
    print("Error was CatError")
}
do {
    try callKids() // throw KidError
} catch is KidError {
    print("Error was KidError")
}

```

In most situations, I’d think that splitting up the error handling like this is something we’d want to _encourage_.

Is there a compelling reason for more compiler magic to allow people to avoid that? Scoping inconveniences with `do`-local variables, maybe, where it becomes necessary to declare a local variable outside the do/catch block and/or nest the do/catch blocks inside a larger `do` block? There might be something there, though I’m skeptical of whether the problem is common enough or serious enough to warrant language gymnastics.

**Edit:** I suppose the more serious motivation would be control flow — and my own rewrite above shows that. 🤦 In a situation where you want the earlier error to bypass the later code but still be exhaustively handled, the alternatives to the aggregated catch block is another pyramid of doom:

```swift
do {
    try callCat() // throws CatError
    do {
        try callKids() // throw KidError
    } catch is KidError {
        print("Error was KidError")
    }
} catch is CatError {
    print("Error was CatError")
}

```

[Next page](https://forums.swift.org/t/se-0413-typed-throws/68507.md?page=2)
