<div dir="ltr"><a href="https://gist.github.com/davbeck/e3b156d89b2e9d97bb5a61c59f8a07f7">https://gist.github.com/davbeck/e3b156d89b2e9d97bb5a61c59f8a07f7</a><br><div><br></div><div><div># Async Await</div><div><br></div><div>I love this proposal so much. Much of it is exactly how I’ve thought Swift’s concurrency model should look over the last year.</div><div><br></div><div>Making async a return attribute just like `throws` seems like the right solution to me. Building on top of callbacks (rather than introducing futures/promises) is also the right approach for swift. I think this proposal nails the problem right on the head: callbacks don&#39;t work well with the rest of Swift&#39;s error handling, is awkward, error prone, and yes, looks ugly.</div><div><br></div><div>One point that I&#39;ve gone back and forth on is how strictly to enforce excecution order. For instance, in this example. it would make sense to allow the first 2 lines to excecute in parallel and excecute the third line once they both complete:</div><div><br></div><div>```swift</div><div>let a = await foo()</div><div>let b = await bar()</div><div>return [a, b]</div><div>```</div><div><br></div><div>But not in this case:</div><div><br></div><div>```swift</div><div>await client.connect()</div><div>let rooms = await client.getRooms()</div><div>```</div><div><br></div><div>In the first case the compiler could automatically optimize to run in parallel, the second, it could not. I like the idea of wrapping parallel code in a future, making the operation explicit and clear.</div><div><br></div><div>### Excecution context</div><div><br></div><div>I’m not familiar with C# or other implementations of async/await, only with Javascript’s (very new) implementation. I’m curious how C# handles execution contexts (threads, queue etc) since JS doesn’t have to deal with that.</div><div><br></div><div>The `syncCoroutine` and `asyncCoroutine` example seems weird to me. It&#39;s also unclear in that example what the context would be after a call to async. Would excecution return to the queue, or be on whatever queue the async function called back on? It makes a lot more sense to me to represent this with code blocks, something like:</div><div><br></div><div>```swift</div><div>doSomeStuff()</div><div>await startAsync(mainQueue) {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>doSomeStuffOnMainThread()</div><div>}</div><div>await startAsync(backgroundQueue) {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>doSomeStuffInBackground()</div><div>}</div><div>```</div><div><br></div><div>Where every line in the code block is run on the context. This doesn&#39;t handle synchronous excecution though. For instance, if we wanted to block a queue until the entire async function had returned. An alternative might be to have queues and other contexts define their own method that took async functions:</div><div><br></div><div>```swift</div><div>doSomeStuff()</div><div>mainQueue.sync {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>await loadSomethingStartingOnMain()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>doSomeStuffOnMainThread()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>// don&#39;t let anything else exceute on the main queue until this line</div><div>}</div><div>await mainQueue.async {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>doSomeStuffInBackground()</div><div>}</div><div>```</div><div><br></div><div>Using `queue.async` taking an async function might be a good alternative to a language &quot;startAsync&quot;. Having the excecution context undefined based on whatever queue the underlying code calls back on seems dangerous to me. Forcing the user to define the context would fix that, but at the cost of introducing extra dispatch calls where they may not be needed. A general purpose context, that simply continued excecution in place would fix that, and be more explicit when it was needed.</div><div><br></div><div>## Conversion of imported Objective-C APIs</div><div><br></div><div>One issue I see with the importer is that the conventions for callbacks aren’t as strict as NSError ** methods were. For instance, URLSession completion blocks include response, data and error, all of which are optionals. The response is almost always present, even if there was an error. But there is no way to know that from the ObjC type system, and no way to represent a throwing function that also returns metadata on error in Swift.</div><div><br></div><div>There are also cases where you wouldn’t want ObjC callbacks to be imported as async functions. For instance, it wouldn’t make sense for NotificationCenter callbacks to be awaited. In general, any callback that can be called more than once is dangerous to use as an async function.</div><div><br></div><div>Personally, I would be in favor of taking a reserved but default on approach to importing ObjC functions as async, and adding annotations to ObjC to control their Swift importing. For instance, by default callbacks with either 0 or 1 arguments would be imported as async non-throwing and callbacks with 0 or 1 arguments plus an error would be imported as throwing async. Callbacks with more than 1 argument would need to be manually annotated. Methods that should not be async (like NotificationCenter) can be annotated to not be imported as async.</div><div><br></div><div>Another issue we’ll need to contend with is intermediate tasks. Both URLSession and the Photos framework come to mind. In the existing model, they return something that allows you to cancel the request while it is in progress. Consider the following example:</div><div><br></div><div>```swift</div><div>class ImageView {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>private var currentTask: URLSessionTask?</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>var source: URL? {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>didSet {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>currentTask?.cancel()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>image = nil</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>guard let source = self.source else { return }</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>load(source: source) { image, error in</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>guard self.source == source else { return }</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>if let image = image {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>self.image = image</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>} else {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>self.image = errorImage</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>var image: Image?</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>func load(source: URL, completion: @escaping (Image?, Error?) -&gt; Void) {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>let task = URLSession.shared.dataTask(with: source) { (data, response, error) in</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>guard let data = data else {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>completion(nil, error)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>return</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>let image = UIImage(data: data)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>completion(image, nil)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>self.currentTask = task</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>task.resume()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>}</div><div>}</div><div>```</div><div><br></div><div>How should we represent dataTask(with:completion:)? If I were writing this API from scratch, task.resume() would be the async function, but that may not be feasible for the importer.</div><div><br></div><div>If I could rewrite that example in a magical future version of Swift and Foundation, it would look something like this:</div><div><br></div><div>```swift</div><div>class ImageView {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>private var currentTask: URLSessionTask?</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>var source: URL? {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>didSet {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>currentTask?.cancel()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>image = nil</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>guard let source = self.source else { return }</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>startAsync {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>do {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>let image = await try load(source: source)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>guard self.source == source else { return }</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>self.image = image</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>} catch {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>guard self.source == source else { return } // kind of awkward to have to write this twice</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                        </span>self.image = errorImage</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>}</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>var image: Image?</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>func load(source: URL) async throws -&gt; Image {</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>let task = URLSession.shared.dataTask(with: source)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>self.currentTask = task</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>let data = await try task.resume()</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                </span>return await UIImage(data: data)</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>}</div><div>}</div><div>```</div><div><br></div><div>## Actors</div><div><br></div><div>I fail to see the benefit of adding an entirely new construct for the actor model. It seems like access control, value semantics, and dispatch queues largely fill this need already, and the implimentation of an actor wouldn&#39;t be so complicated currently that it needs a compiler feature to ensure a correct implimentation.</div><div><br></div><div>Futher, it seems like giving up strict actor models would remove some of the benefits other languages have with their actor models. It has been quit a while since I worked with Erlang, but from what I remember, the magic of it&#39;s recovery model comes from it&#39;s recursive, functional model. It can recover from otherwise fatal errors because it can just reset it&#39;s state to the state before the last message was received. For reasons outlined already, Swift can&#39;t enforce that strictly, so no matter what we are going to have to rely on implimentations to be implimented correctly.</div><div><br></div><div>Perhaps I&#39;m missing something though.</div><div><br></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Aug 17, 2017 at 3:25 PM, Chris Lattner via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><span class=""><br><div><blockquote type="cite"><div>On Aug 17, 2017, at 3:24 PM, Chris Lattner &lt;<a href="mailto:clattner@nondot.org" target="_blank">clattner@nondot.org</a>&gt; wrote:</div><br class="m_2769198905611412516Apple-interchange-newline"><div><div>Hi all,<br><br>As Ted mentioned in his email, it is great to finally kick off discussions for what concurrency should look like in Swift.  This will surely be an epic multi-year journey, but it is more important to find the right design than to get there fast.<br><br>I’ve been advocating for a specific model involving async/await and actors for many years now.  Handwaving only goes so far, so some folks asked me to write them down to make the discussion more helpful and concrete.  While I hope these ideas help push the discussion on concurrency forward, this isn’t in any way meant to cut off other directions: in fact I hope it helps give proponents of other designs a model to follow: a discussion giving extensive rationale, combined with the long term story arc to show that the features fit together.<br><br>Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:<br><a href="https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782" target="_blank">https://gist.github.com/<wbr>lattner/<wbr>31ed37682ef1576b16bca1432ea9f7<wbr>82</a><br></div></div></blockquote></div><br></span><div>Oh, also, one relatively short term piece of this model is a proposal for adding an async/await model to Swift (in the form of general coroutine support).  Joe Groff and I wrote up a proposal for this, here:</div><div><div><a href="https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619" target="_blank">https://gist.github.com/<wbr>lattner/<wbr>429b9070918248274f25b714dcfc76<wbr>19</a></div><div><br></div><div>and I have a PR with the first half of the implementation here:</div><div><a href="https://github.com/apple/swift/pull/11501" target="_blank">https://github.com/apple/<wbr>swift/pull/11501</a></div><div><br></div></div><div>The piece that is missing is code generation support.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>-Chris</div><div><br></div></font></span></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div>