SE-0296: async/await

I'm not sure if it helps, but I see "async/await vs actors" to be analogous to "closures/functions and struct methods".

Functions/closures in Swift can capture arbitrary state, are great at expressing a single action performed against a bag of state, and you can technically define families of them to do things if you try hard enough. However, this quickly becomes a pain when the state you manage is the primary thing, and the actions implemented against that state grow over time.

To solve this, Swift allows you to define a struct, which is a state-primary way of expressing your design, and methods on that struct which can read and/or mutate that state. This is convenient, and extensions allow you to add new behaviors to a bag of state in a retroactive way.

I see async/await in the same way. Simple async functions are great at handling single actions that apply to a specific bag of state (which includes the closed-over values as well as the parameters to the function). Many problems map cleanly onto this.

However, there are other problems where you want to be state-primary: I have a bag of data that I want to reason about, and define actions against it. Actors protect that state from concurrent access and provide a syntactically convenient way to declare the state, declare actions against it, and allow retroactive extensions of those actions.

This duality is a very natural and convenient way to handle things. While we could say (like Java etc) that "every function must be a method on a type", such overhead isn't great. Instead we admit top-level code and global functions to allow direct expression without this overhead. In the world of concurrency, we actually have four things: 1) normal functions that implicitly act against the current actor (which might be a global actor like main), 2) async functions which can be suspended but are otherwise on the current function. 3) async functions that are on an anonymous actor (a member of a "nursery / async let" in the most recent structured concurrency proposal, still TBD), and 4) async methods on actors.

The difference is a spectrum between syncronicity vs asyncronicity on the one hand, and state primary vs action primary on the other hand.

The major payoff of actors (in my opinion) is to way for programmers to think about concurrent programs with a state primary design pattern, just like we have for structs for single threaded designs.

-Chris

15 Likes