SE-0296: async/await

In my personal opinion:

What is your evaluation of the proposal?

+1, with some bike-shedding on the terminology (see below).

Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?

Yes to both. Absolutely.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

In-depth study of the proposal.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Prior to reading the proposal, my intuition of some function that runs "async" was that, when the function is called, it immediately returns a future / promise to the caller, who can then demand the returned value (with await) at a later point. Then, that promise may execute concurrently, depending on resource availability, etc. My intuition for "async" is influenced by code I've written that aligns with this sort of interpretation, e.g., C++'s std::async.

After learning more about the proposal, I discovered that the proposed async marker for function types is actually an indicator of the potential to capture a continuation. The name "async" for this meaning has not yet sat right with me. I agree with @michelf in that a term like "suspends" might make more sense, because it becomes easier (for me) to remember the reasoning behind the rules for async functions. For example, here's an explanation that I can imagine someone would need to write about async functions somewhere in the Swift book:

[...] A suspending function is one that is allowed to pause itself to capture the continuation of the function as a value, called a suspension, that can be resumed at a later time. The code that can access a suspension value is allowed execute on the same thread that captured the suspension. That is, a suspending function can give up its thread for other code to use. Because the dynamic caller of a suspending function is captured as a part of its suspension, all callers of suspending functions must themselves be marked async too.

This type of minimal implementation description helps me keep in mind that async functions are just building blocks for concurrency and parallelism, etc. So, I would be in favor of one of the following terminology changes for async/await with respect to async let from the Structured Concurrency pitch:

  1. Use suspends (or something other than async) in the function type, and keep async let to mean spawning a new task and binding an opaque promise / future, etc. This reserves the term async for features that introduce concurrency.

  2. Keep async for the function type and avoid using async for something like async let (perhaps something like detach let or spawn let instead?). This reserves the term async for a feature that can be used to build concurrency, but detach or spawn actually introduces concurrency.

I prefer (1) but I don't mind (2), either. Especially since it seems that there's already precedent for (2) in existing Swift code, where "async" in a function name means "call me with a completion handler," aka a continuation.

From what I can tell, option (1) creates a cleaner correspondence between Swift's async let / await and async/ await as seen in languages like Javascript and Kotlin. While I've not used it before, it seems that for Javascript, the placement of await makes a difference in whether a call to an async function blocks until the promise is resolved or not. To me, this aligns with Swift's proposed async let, where the point at which one awaits the bound value matters. I'm not sure which languages align with (2), but I'm sure there are some.

tl;dr: My main feedback is that I think it would be confusing to have async used in both the function type to indicate the possibility for suspensions, and in a feature that spawns new concurrent tasks like async let.

1 Like