Thanks for all of the great discussion! I've revised the proposal in a number of ways based on the discussion here, although there is no way I've captured every idea of viewpoint in such a large thread. The general shape of the proposal remains the same, and much of the change is "move a significant piece out to a separate proposal". If you read the last iteration of the proposal, I suggest instead considering the changelist:
Move the the typed rethrows feature out of this proposal, and into Alternatives Considered. Once we gain more experience with typed throws, we can decide what to do with rethrows.
Expand the discussion on allowing all uninhabited error types to mean "non-throwing".
Provide a better example for inferring Error conformance on generic parameters.
Move the replacement of rethrows in the standard library with typed throws into "Future Directions", because it is large enough that it needs a separate proposal.
Move the concurrency library changes for typed throws into "Future Directions", because it is large enough that it needs a separate proposal.
Add an extended example of replacing the need for rethrows(unsafe) with typed throws.
Provide a more significant example of opaque thrown errors that makes use of Either internally.
Given the unapproachable size of the current thread (261 posts!) and the change in scope of this new pitch, wouldn't it be a good idea to start a new thread about it to recenter the discussion?
I donāt follow. Isnāt @discardableResult about being able to pretend a function that returns a value were a Void function instead? Maybe I didnāt communicate it clearly.
Iād say the main advantage is that it could exist now, in a world without typed throws, and could be used to mark your API for consumers of it to know what errors the API expects them to handle while not painting yourself into a corner with a type that canāt be changed later as your library evolves.
I thought it might meet the needs of that segment of folks wanting that information to be ābaked inā to the API, while still keeping the throws as any Error for simplicity. Iām no expert here, so I could be way off base.
I posted here because I'm mostly moving pieces out to Future Directions and adding clarifications for things that came up in this thread. It doesn't seem different enough to kick off a new pitch thread where I expect folks to go and read a significant-different proposal, especially given the length of the document.
Iām just coming back to this thread over a week later, so apologies if this was already discussed and I just missed it, but: DispatchQueue.sync already uses a weird hack that shouldnāt work to be rethrows. How would typed throwing affect that?
Ah, thatās great! Itās intuitive at the source level but what ABI impact would it have? Adding a new generic type argument isnāt an ABI-stable change, is it?
I could not find this raised (but I'm sorry if this is already discussed). Should we explicitly write catch any Error to prevent future source breakage?
I suspect there can be some possibility to introduce errorUnion(CatError, KidError) and make error this type as written in the proposal.
Rationale: While it would be possible to compute a more precise "union" type of different error types, doing so is potentially an expensive operation at compile time and run time, as well as being harder for the programmer to reason about. If in the future it becomes important to tighten up the error types, that could be done in a mostly source-compatible manner.
I don't know what 'mostly source-compatible manner' points, but I found the following breakage case. As written in the proposal, when multiple try occurs in the same do clause, they become any Error in the catch clause. However, if we introduce errorUnion, the catch clause will implicitly get errorUnion(CatError, KidError) which is not convertible with GenericError .
do {
try callCat() // throws CatError
try callKids() // throw KidError
} catch {
var e = error
e = GenericError(message: "")
}
I think it's possible to consider adding explicit any Error notation to avoid such breakage.
do {
try callCat() // throws CatError
try callKids() // throw KidError
} catch any Error { // or `let error as any Error`
// implicit 'error' variable has type 'any Error'
}
I have to admit that it is redundant for now, and there are pros and cons, but I think it needs to be considered.
Just want to quickly chime in and say that I love every detail of this proposal! I think itās very balanced and adds the #1 feature I currently miss in Swift.
The only thing I think could be improved is the āwhen (not) to useā section which does not explicitly mention which group iOS/macOS apps are in. While itās clear to me that itās basically a module that will not be integrated into another one and therefore is a great place to use typed throws, it might not be clear to less informed developers and mentioning iOS apps explicitly could help clarify things.
Also, the section states that in most cases it does not make sense to use them. But app development being a huge use case for Swift, I kinda disagree as it can make a lot of sense to use typed throws in app modules and ā at least in my bubble ā most developers use Swift in app projects. Only a minority (outside Apple) writes Swift packages or other kind of libs from my experience.
But thatās a detail that doesnāt bother me much. I really hope this will become an official proposal soon so we can get it in the next Swift release!
I'm really hopeful that Foundation APIs and other major frameworks can adopt typed throws. I think that will slowly trickle up the layers, making existential throws less necessary and less common. To echo Jon Shier's earlier comment, erasing to any Error is an escape hatch that's (unfortunately) the only reasonable choice today. I look forward to having access to explicitness ā top to bottom ā without resorting to Result.
Any time a user (whether end user or library user) is in some way responsible for the error and / or needs to (or could) do something to resolve it in a subsequent action the possibility of this error should be clearly documented so apps may handle it and provide useful information to the user. I have encountered countless times as both an end user and a library user where this information is absent or very difficult to find. This results in a needlessly frustrating end user experience.
This could provide a useful heuristic for deciding when to use typed errors. The only alternative is providing this information via documentation. My experience has been that libraries that do this well are rare. Establishing this as a conventional heuristic for typed throws would encourage good behavior throughout the ecosystem.
A good example is credit card expiration. Apps do not always report the reason for payment failure even when this information is available to the payment provider. Providing typed errors that clearly represent the reason for failure would encourage better behavior and be very helpful to end users.
I think it's best we just wait and see, now. We all need real-world experience to inform our opinions any further.
I've been following @Douglas_Gregor's implementation of this and it's exciting to see it coming along, although I have no idea how far away it is from being integrated. It might lead to my first real use of the Swift nightly build, though, once it's in there - I'm very interested to see how it plays out in practice in a couple of projects I'm currently working on.
When I first heard of this proposal I didnāt like the sound of it because I was weary of having to deal with a more complex error type system like other languages. But after reading the proposal and most of the comments here it really seems like a big improvement especially with the ability to have one type for a throwing vs non throwing class like everyone is mentioning. And it doesnāt bleed through and cause all functions to need to be typed.
Any timeline on when we might be able to use Typed Throws with standard Xcode Swift Toolchain and ship it in an iOS (16) app? Beginning of next year? Or post WWDC?
Typed throws is under review, and the Language Steering Group has not yet posted their decision on it. It's being implemented on main, so the earliest it could make it into into a released compiler would be Fall 2024. Snapshots of main already have it under an experimental flag TypedThrows, and can be downloaded from swift.org.