[swift-evolution] async void

Adam Kemp adam_kemp at apple.com
Fri Nov 10 20:02:10 CST 2017


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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171110/8168a17a/attachment.html>


More information about the swift-evolution mailing list