[swift-evolution] async void

Adam Kemp adam_kemp at apple.com
Mon Nov 13 11:52:53 CST 2017


However, equally important to the case I was making earlier is that you should be able to call an “async Never” function as if it were a non-async Void function. This is especially important for cases where you want to pass a callback to something (like an event handler), and you want the callback to be able to use await. So as long as that still works I could live with "async Never”.

> On Nov 13, 2017, at 9:49 AM, Adam Kemp <adam_kemp at apple.com> wrote:
> 
> I kind of like that idea. I’d rather have a Task/Future/Promise/whatever, but if that’s not going to happen then Void vs. Never seems like a reasonable way of distinguishing these two cases.
> 
>> On Nov 12, 2017, at 9:55 AM, Xiaodi Wu <xiaodi.wu at gmail.com <mailto:xiaodi.wu at gmail.com>> wrote:
>> 
>> Sorry, I'm just getting into this conversation late and am by no means experienced in the area, but why can't the one where you *don't* want the caller to wait for the result be spelled `async -> Never`? Theoretically, `async -> Void` means you're awaiting a result with only one possible value, but if you're not waiting at all, then there is truly no result, yes?
>> 
>> 
>> On Sun, Nov 12, 2017 at 9:27 AM, Yuta Koshizawa via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> Sorry, I had got some confusion. Please let me retry to explain.
>> 
>> As you said, C# provides three kinds of async functions: `async Void`,
>> `async Task` and `async Task<Foo>`. All of them are necessary and
>> Swift should provide same functionalities.
>> 
>> When we think about `async/await` in Swift, because we have already
>> had `throws/try`, it is desired that `async/await` in Swift is
>> consistent with `throws/try`. So it is better to have `async/await`
>> without introducing a type like `Task` (or `Promise`).
>> 
>> Even if we employ `async/await` without `Task`, Swift has to provides
>> functionalities to implement "three kinds of async functions" in C#.
>> However if `async -> Void` in Swift works similarly to `async Void` in
>> C#, how can we express ones like `async Task` in C#? I think there are
>> two possibilities:
>> 
>> 1. Calling `async -> Void` functions without `await` in Swift works
>> like `async Void` in C# and calling them *with* `await` works like
>> `async Task` in C#.
>> 2. Calling `async -> Void` functions without `await` in Swift works
>> like `async Void` in C# and never support something like `async Task`
>> in C#.
>> 
>> I think 2 is impermissible. For example, handling completion events of
>> asynchronous operations without result values needs something like
>> `async Task` in C#. However, with 1, we lose the benefit of static
>> checks by the compiler. Because both of `fooAsync()` without `await`
>> and `await fooAsync()` are allowed, even if we want it to work like
>> `async Task` in C# and forget to mark `await`, the compiler tell us
>> nothing and it works like `async Void` in C#. It causes unexpected
>> behaviors. It is hard to fix such kinds of bugs. So I think
>> introducing `beginAsync` is better.
>> 
>> --
>> Yuta
>> 
>> 
>> 2017-11-12 10:23 GMT+09:00 Yuta Koshizawa via swift-evolution
>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
>> > 2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.kemp at apple.com <mailto:adam.kemp at apple.com>>:
>> >>
>> >>
>> >>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher at koherent.org <mailto: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
>> > _______________________________________________
>> > swift-evolution mailing list
>> > swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> > https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171113/2c200957/attachment.html>


More information about the swift-evolution mailing list