[swift-evolution] async void

Yuta Koshizawa koher at koherent.org
Sat Nov 11 08:24:29 CST 2017


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.


> 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 = { ... }
```

--
Yuta


2017-11-11 11:02 GMT+09:00 Adam Kemp <adam_kemp at apple.com>:
> I’m not sure that answered my questions.
>
> Can I do this?
>
> let asyncClosure:() async -> Void = { await doSomethingAsync() } // Can you
> declare an async closure variable?
>
> beginAsync(asyncClosure)
>
>
> What about this?
>
> let syncClosure:() -> Void = { doSomethingSync() }
>
> beginAsync(syncClosure) // Can a non-async closure be passed to a function
> expecting a async closure?
>
>
> And this?
>
>
> func callSyncClosure(_ closure:() -> ()) { closure() }
>
>
> let asyncClosure:() async -> Void = { await doSomethingAsync() }
>
>
> callSyncClosure(asyncClosure) // Can an async closure be passed to a
> function expecting a non-async closure?
>
>
> Here are my thoughts on each of these:
>
> Can you declare an async closure variable? If async is part of the type then
> it would be very strange not to allow this. I think the proposal implies
> that you can, but only by way of an example about inferring whether a
> closure is async or not.
>
> Can a non-async closure be passed to a function expecting a sync closure?
> This seems like an obvious conversion. I feel like disallowing this would
> probably make things harder, but I haven’t thought it through enough to say
> exactly how. Are there reasons that this shouldn’t work, though?
>
> Can an async closure be passed to a function expecting a sync closure? This
> may be controversial, but I think this should be allowed too. I think a
> caller that expects a void-returning synchronous function should be able to
> call an async function and treat it as a call-and-forget.
>
> It’s weird to me that we would allow you to have async void closures but not
> async void functions, but in order to support beginAsync we have to have
> async void closures. So if we can make that work then why couldn’t we just
> make async void functions work and dispense with beginAsync entirely?
>
> On Nov 10, 2017, at 1:38 AM, Yuta Koshizawa via swift-evolution
> <swift-evolution at swift.org> wrote:
>
> I’m not sure how the proposed design handles type checking for async
> closures. Is async part of the type? Can I declare a local closure variable
> as async? What are the rules for conversion between async and non-async
> closures? Is it a warning or error to use a closure literal without an async
> keyword that is assigned to an async closure variable or passed as an async
> closure argument?
>
>
> It has been already realized for `throws`.
>
> ```
> // This works in Swift 4
> func foo(_ f: (Bar) throws -> Baz)
>
> // This can be checked in the same way
> func foo(_ f: (Bar) async -> Baz)
> ```
>
> Proposed `async/await` in Swift are analogous to `throws/try`. So a
> lot of things about `async/await` can be imagined when we think about
> `throws/try`. It is also true for `(Foo) async -> Void`. We can use it
> in the same way as we use `(Foo) throws -> Void`.
>
> `async/await` as an analogy to `throws/try` works well because both of
> them can be mapped to monads. `throws` is similar to `Result` and
> `async` is similar to `Promise` ( `Future` ).
>
> ```
> // `a` and `b` are similar
> func a() throws -> Int
> func b() -> Result<Int>
>
> // `c` and `d` are similar
> func c() async -> Int
> func d() -> Promise<Int>
>
> // `a` : `b` == `c` : `d`
> // `a` : `c` == `b` : `d`
> ```
>
> `try` and `await` are also similar to `flatMap`. ( I think most
> popular `Promise` is one in JavaScript. Although it does not have
> `flatMap`, its `then` method can be considered as `flatMap`. )
>
> ```
> let x = try a()
> // uses `x` here
>
> b().flatMap { x in
>  // uses `x` here
> }
>
> let y = await c()
> // uses `y` here
>
> d().flatMap { y in
>  // uses `y` here
> }
> ```
>
> So `throws` : `try` : `Result` == `async` : `await` : `Promise` and
> `throws` : `async` == `try` : `await` == `Result` : `Promise`. I think
> those relations are beautiful and proposed `async/await` fits well to
> Swift because we have already had `throws/try`.
>
> --
> Yuta
>
>
> 2017-11-10 6:45 GMT+09:00 Adam Kemp via swift-evolution
> <swift-evolution at swift.org>:
>
>
> On Nov 9, 2017, at 11:02 AM, Wallacy <wallacyf at gmail.com> wrote:
>
> So for me, this:
>
> func OnButtonClicked(_ sender:AnyObject) {
>
>    let button = sender as! UIButton
>    button.isEnabled = false
>    beginAsync {
>        await DoSomethingAsync()
>        button.isEnabled = true
>    }
> }
>
>
> Does not make any sense... Because await will not block the thread we can
> assume (like the exemple on proposal) that compiler will actually do the
> same what we will do using GDC and pick everyone to until the context ends
> and encapsulate as completion handle to the away call.
>
>
> I don’t understand what you’re saying here. In terms of GCD the above code
> is basically equivalent to this:
>
> func buttonClicked(_ sender:AnyObject) {
>    let button = sender as! UIButton
>    button.isEnabled = false
>    doSomethingAsync { // completion callback, assume it’s on the main queue
>        button.isEnabled = true
>    }
> }
>
>
> And this one:
>
> @IBAction func buttonDidClick(sender:AnyObject) {
>  beginAsync {
>    let image = await processImageData()
>    // Do the update on the main thread/queue since it owns imageView.
>    mainQ.async {
>      imageView.image = image
>    }
>  }
> }
>
>
>
> Does not make any sense too.
>
> The only way to await to do not block the thread is make a early return like
> beginAsync and encapsulate as completion handle the rest of the code.
>
>
> This is why “await” can only be used in a function that is marked as
> “async”. The “async” keyword tells the compiler that this function will be
> broken up into parts, and its return value will be produced asynchronously.
>
> One thing that might be confusing is that in the examples using “beginAsync”
> the trailing closure is the async function. That’s not obvious because
> there’s no “async” keyword, but it’s there in the signature of the closure
> argument:
>
> func beginAsync(_ body: () async throws -> Void) rethrows -> Void
>
>
> I’m not sure how the proposed design handles type checking for async
> closures. Is async part of the type? Can I declare a local closure variable
> as async? What are the rules for conversion between async and non-async
> closures? Is it a warning or error to use a closure literal without an async
> keyword that is assigned to an async closure variable or passed as an async
> closure argument?
>
> C# dodges all of these issues because async isn't part of the type system.
> Closures that don’t return anything can be declared async just like void
> functions, and code that uses void-returning closures doesn’t need to care
> whether the implementation is async or not.
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>


More information about the swift-evolution mailing list