Conditional async? Like rethrows but for async

I'm trying to figure out if there's a way/plan to do something like this:

func foo(bar: () -> Void) {
    bar()
}

func testConditionalAsync() async {
    foo { //await not needed because closure body doesn't contain any await either
        print("not async")
    }

    await foo { //await needed because closure body does contain an await
        await someAsyncJob()
    }
}

I know that it is possible to achieve this result with function overloads (one sync, one async), but I was hoping to see something similar as throws+rethrows, when the compiler can determine the need for an await. I tried to see if it was ever discussed, but I couldn't find anything.

If it's something new but should be possible (I need help with that) then I'm happy to work on a pitch for that.

2 Likes

It's needed, but given that rethrows is kinda obsolete, I'm not sure if matching it is the best mental model*.


* Maybe it's fine? If one of them is async, then the whole thing is. That's different than rethrows, which loses error typing when multiple error types are equivalent. With async, there are no types to lose.

func f<E1, E2>(
  _: () throws(E1) -> Void,
  _: () throws(E2) -> Void
) rethrows { }

func no() { }
struct E: Error { }
func thro() throws(E) { }
f(no, no) // rethrows makes this possible, not matching `Never`s
try f(no, thro) // throws `any Error`, not `E`

// The error situation is still a problem, but the `async` isn't.
func f<E1, E2>( 
  _: () throws(E1) -> Void,
  _: () async throws(E2) -> Void
) reasync rethrows  { }
1 Like