I will be happy to help again if the core team feels like it is necessary to do so, especially if it can avoid an "Accepted with modifications" conclusion :-)
Yes.
And yes.
Doug
I agree!
We'd definitely appreciate that! I consider this a clarification of the existing semantics, so you can update the PR with this if you'd like.
Doug
Do you know if the suggested fix-its will include both options by default or if there is any way to know that you've only implemented one of the two function requirements of the protocol? Or is there any way to mark that you'd like the sync version to also apply for the async
. I often rely pretty heavily on the compiler to help me implement protocols, so I would hope that it would fill in both as options, letting you delete one, or warning you in some way that you might be shadowing an async
function requirement.

We'd definitely appreciate that! I consider this a clarification of the existing semantics, so you can update the PR with this if you'd like.
Done in this commit. cc @xwu
(EDIT: fixed commit link after a force-push)
Great. And would you mind including the example where a closure can be used to select the synchronous overload in an asynchronous context (and, I think, binding the function to an explicitly typed variableâunless I misunderstand)?
This example is already there, I guess you missed it. If you have other requests, let's use the same process: someone suggests an improvement, then one author of the proposal agrees, and I help if I can. Thank you.

This example is already there, I guess you missed it.
Sorry, Iâm still not seeing what youâre referring to; the commit youâve linked me to has 23 lines added (at least thatâs what GitHub is showing me). Can you point out where the example is? Thanks!
I think @gwendal.roue is referring to this snippet:
func f() async {
let f2 = {
// In a synchronous context, the non-async overload is preferred:
doSomething()
}
f2()
}
Does this not satisfy the request of an "example where a closure can be used to select the synchronous overload in an asynchronous context"? Or were you thinking something like this:
func f() async {
let doSomething: () -> Void = doSomething
doSomething()
}

Does this not satisfy the request of an "example where a closure can be used to select the synchronous overload in an asynchronous context"? Or were you thinking something like this:
func f() async { let doSomething: () -> Void = doSomething doSomething() }
Both, but indeed I misread the second code example--maybe it was the lack of code highlighting that tripped me up (I didn't see the async
because it's highlighted in the first example but not the second, and the comment then says "In a synchronous context..."). Thanks!

Should the same affordance be extended to throw'ing functions?
This question hasn't been answered, but I suppose that that proposal reviews are not discussions. As such, I'll say this another way:
I am -1 on the proposal unless we extend this to the throw
effect, if we add both then I am +1.
Rationale: I believe that it is important to allow async overloading, but even more important to keep the language consistent about how it handles effects. Inconsistency makes the language more complex and makes future things more difficult to add.
Is there any disadvantage to allowing overloading on throws
?
-Chris
Is it OK to condition the acceptance of this amendment to the relaxing of the overload rules for the throws
effect, given this review ends tomorrow and we don't have much time?
Anyway, schedule is not that important, and I would support Chris' idea. The discussed SE-0296 proposal describes how the compiler selects one and only overload given the context of the caller. Some functions are async
, some are non-async
, and this is how the compiler can select an overload:
func f() {
// non-async function -> non-async overload selected
doSomething()
}
func f() async {
// async function -> async overload selected
await doSomething()
}
This extends well to the selection of the one and only throwing/non-throwing overload:
func f() {
// non-throwing function -> non-throwing overload selected
doSomething()
}
func f() throws {
// throwing function -> throwing overload selected
try doSomething()
}
Some details would need to be clarified, though, especially regarding rethrows
functions.
The non-throwing overload has to be selected in order to avoid throwing when the an input closure does not throw:
func doSomething() { ... }
func doSomething() throws { ... }
func f(_ closure: () throws -> Void) rethrows {
doSomething() // non-throwing overload selected
}
It gets funnier when the overloaded function is itself rethrows
:
func doSomething(_ closure: () -> Void) { ... }
func doSomething(_ closure: () throws -> Void) rethrows { ... }
func f(_ closure: () throws -> Void) rethrows {
try doSomething(closure) // throwing overload selected
doSomething { } // non-throwing overload selected
}
Maybe the same question will happen for reasync
when it is introduced.
Thanks Gwendal

Some details would need to be clarified, though, especially regarding
rethrows
functions.
Right, this is the sort of thing we need a solid model for. We're likely to want to continue building out the "reasync" model as well, so learning from the existing throws world can help make sure the model we're going towards is fully considered,
-Chris
I donât think consistency is enough motivation to allow overloading on throws
. Why is it different? Because we already have tools to call a throwing function in a non-throwing context: Result and do
/catch
. Thereâs (deliberately) no equivalent for an async
API. Meanwhile, we have all these synchronous functions with the âgoodâ names; thatâs the only reason we want to add overloading. Thatâs not a problem for throwing functions. (Well, it was when throwing was introduced, but not now.)

Because we already have tools to call a throwing function in a non-throwing context: Result and
do
/catch
. Thereâs (deliberately) no equivalent for anasync
API. Meanwhile, we have all these synchronous functions with the âgoodâ names; thatâs the only reason we want to add overloading. Thatâs not a problem for throwing functions. (Well, it was when throwing was introduced, but not now.)
I do agree with you that there is less of a "why now" driven by "all the good names are taken" argument, but that doesn't argue against generalizing throws -- it only provides justification for making a change (which I'm in favor of as I mentioned!).
I don't understand the other part of your argument. We do have an equivalent for Result, the oddly named Task
type. Similarly, we have the oddly named "run detached" functionality which allows calling an async function from a sync function. Furthermore, the "async functions aren't as baked out as throwing functions" argument seems like it should be better solved by baking async functions, not by making them inconsistent with the more baked part of the language.
-Chris
I guess I donât consider Task and such equivalent because they donât let you get a result synchronously (i.e. they donât provide a join()
). Which, 9 times out of 10, is a good thing, Iâll re-iterate!
Honestly, Iâm ambivalent about this whole proposal. Itâs a practical answer to a problem, though perhaps reasync
would be a more interesting one for some of these (like the XCTest methods). But one of the reasons why it makes more sense for async
than throws
is that when youâre in an async function, youâre (probably) already thinking of suspending, and so biasing towards an async overload makes sense. The caller cannot really tell the difference, as long as youâre careful about your suspension points. But Iâm not convinced that when youâre in a throwing function, youâd automatically rather call a throwing overload, and vice versa for a non-throwing function and a non-throwing overload. I think thatâll lead to people picking the wrong overload in a way that doesnât apply for async
. Thatâs assuming, of course, it makes sense to have a non-throwing version of a throwing method at all. In ObjC Cocoa we got them as leftovers from the migration from NSException to NSError, or in returning a Bool to producing an NSError; neither of these apply to modern ObjC Cocoa APIs, let alone Swift.
I'm also waiting for people who want typed throws
to chime in. Typed throws
would send us from two effects (throws
and rethrows
) to an infinity of effects, with complex sub-typing relationships, and very potentially a much more complex overloading rule.
I now think that the topic of "allowing overloads that differ only in throws
" should be moved on its own dedicated thread. A very interesting one
I'm no less concerned than others about introducing a new language inconsistency. But async
overloads do not need the rule to be extended to all effects to be useful.

I'm also waiting for people who want typed
throws
to chime in.
I donât have the time to write a complete response right now, but in swift-system
it would be greatly preferable to write throws Errno
to throws
. This would avoid an existential box and improve callers understanding of the class of errors that could occur from the method. Additionally, typed throws aligns much better with Result
which is a much more ergonomic than do/try/catch
when manipulating errors.
I don't think that it is as important to allow overloads of throws
functions as it is for async
functions.
Consider the natural non-throwing and non-asynchronous overloads for the following functions:
func foo() throws -> Int { /* ... */ }
func bar() async -> Int { /* ... */ }
IMHO they would look like this:
func foo() -> Int? { /* ... */ }
func bar() -> Int { /* ... */ }
Notice that the overload of foo()
is already allowed under the current overloading rules, because it has a different return type, while we get an error for the overload of bar()
.
This, in combination with the fact that there are multiple ways to call a throwing function in a non-throwing context, convinces me that we can with good conscience introduce async
overloads without introducing the same for throws
.
EDIT:
Of course I would also support the decision to introduce overloads that differ only in throws
, if that is really necessary. I just do not see a compelling reason for it at the moment.

I'm also waiting for people who want typed
throws
to chime in. Typedthrows
would send us from two effects (throws
andrethrows
) to an infinity of effects, with complex sub-typing relationships, and very potentially a much more complex overloading rule.
I'm a fan of typed throws and hope we add it, but I don't think we should do what you're suggesting here. Overloading would only be allowed on "throws or not", we shouldn't have a way to overload on "what is thrown". We support overloading on return type and even that is dubious. I don't think there is any justification to support overloading on "what is thrown".
-Chris