> On Aug 18, 2017, at 8:38 PM, Chris Lattner <clatt...@nondot.org 
> <mailto:clatt...@nondot.org>> wrote:
> 
> On Aug 18, 2017, at 2:09 PM, Adam Kemp <adam.k...@apple.com 
> <mailto:adam.k...@apple.com>> wrote:
>> Maybe I’m still missing something, but how does this help when you are 
>> interacting only with Swift code? If I were to write an asynchronous method 
>> in Swift then how could I do the same thing that you propose that the 
>> Objective-C importer do? That is, how do I write my function such that it 
>> calls back on the same queue?
> 
> You’re right: if you’re calling something written in Swift, the ObjC importer 
> isn’t going to help you.
> 
> However, if you’re writing an async function in Swift, then it is reasonable 
> for us to say what the convention is and expect you to follow it.  
> Async/await doesn’t itself help you implement an async operation: it would be 
> turtles all the way down… until you get to GCD, which is where you do the 
> async thing.
> 
> As such, as part of rolling out async/await in Swift, I’d expect that GCD 
> would introduce new API or design patterns to support doing the right thing 
> here.  That is TBD as far as the proposal goes, because it doesn’t go into 
> runtime issues.

The point I’m trying to make is that this is so important that I don’t think 
it’s wise to leave it up to possible future library improvements, and 
especially not to convention. Consider this example again from your proposal:

@IBAction func buttonDidClick(sender:AnyObject) {  
    doSomethingOnMainThread();
    beginAsync {
        let image = await processImage()
        imageView.image = image
    }
    doSomethingElseOnMainThread();
}

The line that assigns the image to the image view is very likely running on the 
wrong thread. That code looks simple, but it is not safe. You would have to 
insert a line like your other examples to ensure it’s on the right thread:

@IBAction func buttonDidClick(sender:AnyObject) {  
    doSomethingOnMainThread();
    beginAsync {
        let image = await processImage()
        await DispatchQueue.main.asyncCoroutine()
        imageView.image = image
    }
    doSomethingElseOnMainThread();
}

You would have to litter your code with that kind of stuff just in case you’re 
on the wrong thread because there’s no way to tell where you’ll end up after 
the await. In fact, this feature would make it much easier to end up calling 
back on different queues in different circumstances because it makes queue 
hopping invisible. From another example:

func processImageData1() async -> Image {
  let dataResource  = await loadWebResource("dataprofile.txt")
  let imageResource = await loadWebResource("imagedata.dat")
  let imageTmp      = await decodeImage(dataResource, imageResource)
  let imageResult   =  await dewarpAndCleanupImage(imageTmp)
  return imageResult
}

Which queue does a caller end up in? Whichever queue that last awaited call 
gives you. This function does nothing to try to ensure that you always end up 
on the same queue. If someone changes the code by adding or removing one of 
those await calls then the final callback queue would change. If there were 
conditionals in there that changed the code flow at runtime then you could end 
up calling back on different queues depending on some runtime state.

IMO this would make doing safe async programming actually more difficult to get 
right. It would be tedious and error prone. This simplified async/await model 
may work well for JavaScript, which generally doesn’t have shared mutable state 
across threads, but it seems dangerous in a language that does.

> This isn’t a fair transformation though, and isn’t related to whether futures 
> is part of the library or language.  The simplification you got here is by 
> making IBAction’s implicitly async.  I don’t see that that is possible, since 
> they have a very specific calling convention (which returns void) and are 
> invoked by objc_msgSend.  OTOH, if it were possible to do this, it would be 
> possible to do it with the proposal as outlined.

I didn’t mean to imply that all IBActions implicitly async. I just allowed for 
an entire method to be async without being awaitable. In C# an async void 
function is a “fire and forget” function. It executes in the context of the 
caller’s thread/stack up until the first await, at which point it returns to 
the caller like normal. The continuation just happens without the caller 
knowing about it. The method signature is the same, and they are callable by 
code that is unaware of async/await. C# supports async void functions 
specifically for the event handler use case (and it is generally discouraged 
for all other use cases).

Your proposal already has async void methods, but they are awaitable. You still 
need some ability to call an async method from a non-async method. The way that 
you solved that is a special function (beginAsync), which as I described 
earlier has some issues with readability. The other approach is to have some 
way to decorate the entire function as “fire and forget”.

The reason I linked these to library support is that the way that C# makes this 
distinction is that an awaitable function must return a type that is awaitable 
(it’s actually the traits of the return type that make it awaitable, not the 
async keyword; you can await a function that is not marked async). In C# a void 
method is not awaitable, even if it is marked async.

However, you’re right that these concepts don’t necessarily have to be linked, 
and I shouldn’t have conflated them. You could also use some other syntax to 
mark a non-awaitable method that is itself async. Maybe a different keyword 
(deliberately ugly):

@IBAction fire_and_forget_async func buttonDidClick(sender:AnyObject) { … }

To be clear, although this particular problem can be solved without library 
support I think that both this problem and the queue-hopping problem could be 
better solved by using a model closer to C#’s, with a richer compiler transform 
that uses the return type. Swift could do this using an awaitable protocol.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to