[swift-evolution] async void

Yuta Koshizawa koher at koherent.org
Sat Nov 11 19:23:13 CST 2017


2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.kemp at apple.com>:
>
>
>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher at koherent.org> wrote:
>>
>> If you replace `async` with `throws`, you can get answers.
>>
>>
>>> Can you declare an async closure variable?
>>
>> Yes. Like `let throwingClosure:() throws -> Void = { ... }`.
>>
>>
>>> Can a non-async closure be passed to a function expecting a async closure?
>>
>> Yes. Like we can pass `() -> Void` to a function expecting a throwing
>> closure `() throws -> Void`.
>>
>> It is possible because `(Foo) throws -> Bar` is a supertype of `(Foo)
>> -> Bar`. `(Foo) async -> Bar` is a supertype of `(Foo) -> Bar` in the
>> same way.
>>
>> To treat an async function as a sync function is legal. It is similar
>> to make a `Promise` by `Promise(value)` which is completed
>> immediately.
>>
>>
>>> Can an async closure be passed to a function expecting a non-async closure?
>>
>> No. `() -> Void` is a subtype of `() async -> Void`. It is same as
>> passing `() throws -> Void` to a function expecting `() -> Void` is
>> not allowed.
>
> But why not? Just asserting that it must work the same as throws
> is not a convincing argument. You have to justify why it must work
> that way. I think there is good reason to allow it, which I have described.
> What reason is there to disallow it?

`() async -> Void` needs to be called with `await` because it prevents
us from forgetting handling asynchronous operations.

If we use callbacks to handle asynchronous operations, it is shown to
us by a compiler as a compilation error.

```
func fooAsync(_ handler: () -> Void) -> Void { ... }

fooAsync() // compilation error

fooAsync {
  // handles a completion event here
}
```

With proposed `async/await`, it is realized similarly like below.

```
func fooAsync() async -> Void { ... }

fooAsync() // compilation error

await fooAsync()
// handles a completion event here
```

However, if async void functions work like `beginAsync`, we can easily
forget it and it can cause unexpected behaviors.

```
func fooAsync() async -> Void { ... }

fooAsync() // OK
// hard to know this line is executed asynchronously
```

Readability also suffers seriously. If we don't know `bar` in the
following code is a async function, it is impossible to expect lines
after `baz()` are executed asynchronously.

```
foo()
bar()
baz()
qux()
```


>>> It’s weird to me that we would allow you to have async void closures but not async void functions
>>
>> I am not sure what you mean. "async void closures" and "async void
>> functions" have a same type. Following two are almost same.
>>
>> ```
>> func foo() async -> Void { ... }
>> let foo: () async -> Void = { ... }
>> ```
>
> What started this thread is my suggestion that you should be able to write
> an async void function. The current proposal doesn’t allow that. That’s why
> you have to use beginAsync.
>
> I don’t think that makes sense. It sounds like you also think that would be strange,
> hence your assumption that you could.

By the reasons I wrote above, we need `await` even for async void
functions for checks by compilers. Then it is required to provide a
way to write entry points of async functions. That is `beginAsync`.

--
Yuta


More information about the swift-evolution mailing list