[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