[swift-evolution] [Concurrency] async/await + actors

Matthew Johnson matthew at anandabits.com
Fri Aug 18 08:12:08 CDT 2017

Sent from my iPad

> On Aug 17, 2017, at 11:53 PM, Chris Lattner <clattner at nondot.org> wrote:
>> On Aug 17, 2017, at 7:39 PM, Matthew Johnson <matthew at anandabits.com> wrote:
>> This is fantastic!  Thanks for taking the time to write down your thoughts.  It’s exciting to get a glimpse at the (possible) road ahead.
> Happy to.
>> In the manifesto you talk about restrictions on passing functions across an actor message.  You didn’t discuss pure functions, presumably because Swift doesn’t have them yet.  I imagine that if (hopefully when) Swift has compiler support for verifying pure functions these would also be safe to pass across an actor message.  Is that correct?
> Correct.  The proposal is specifically/intentionally designed to be light on type system additions, but there are many that could make it better in various ways.  The logic for this approach is that I expect *a lot* of people will be writing mostly straight-forward concurrent code, and that goal is harmed by presenting significant type system hurdles for them to jump over, because that implies a higher learning curve.
> This is why the proposal doesn’t focus on a provably memory safe system: If someone slaps “ValueSemantical” on a type that doesn’t obey, they will break the invariants of the system.  There are lots of ways to solve that problem (e.g. the capabilities system in Pony) but it introduces a steep learning curve.
> I haven’t thought a lot about practically getting pure functions into Swift, because it wasn’t clear what problems it would solve (which couldn’t be solved another way).  You’re right though that this could be an interesting motivator. 

I can provide a concrete example of why this is definitely and important motivator.  

My current project uses pure functions, value semantics and declarative effects at the application level and moves as much of the imperative code as possible (including effect handling) into library level code.  This is working out really well and I plan to continue with this approach.  The library level code needs the ability to schedule user code in the appropriate context.  There will likely be some declarative ability for application level code to influence the context, priority, etc, but it is the library that will be moving the functions to the final context.  They are obviously not closure literals from the perspective of the library.

Pure functions are obviously important to the semantics of this approach.  We can get by without compiler verification, using documentation just as we do for protocol requirements that can't be verified.  That said, it would be pretty disappointing to have to avoid using actors in the implementation simply because we can't move pure functions from one actor to another as necessary.

To be clear, I am talking in the context of "the fullness of time".  It would be perfectly acceptable to ship actors before pure functions.  That said, I do think it's crucial that we eventually have the ability to verify pure functions and move them around at will.

>> The async / await proposal looks very nice.  One minor syntax question - did you consider `async func` instead of placing `async` in the same syntactic location as `throws`?  I can see arguments for both locations and am curious if you and Joe had any discussion about this.
> I don’t think that Joe and I discussed that option.  We discussed several other designs (including a more C# like model where async functions implicitly return Future), but he convinced me that it is better to focus language support on the coroutine transformation (leaving futures and other APIs to the library), then you pretty quickly want async to work the same way as throws (including marking etc).  Once it works the same way, it follows that the syntax should be similar - particularly if async ends up implying throws.
> That said, if you have a strong argument for why this is perhaps the wrong choice, lets talk about it!

I don't necessarily have a *strong* argument and don't want to see us get to sidetracked by a bikeshedding exercise at this point either.  :)

Briefly, here is my perspective.

In favor of the proposed syntax:
* From the point of view of the caller, it is the result that is async.
* As you noted, if `async` implies `throws` it is the only reasonable choice.
* It provides consistency with `throws` and perhaps establishes a standard syntactic location for additional effect specifiers down the road.

In favor of `async func`:
* `async` is arguably a much more significant modifier to the behavior of the function than `throws`, influencing the entire execution model, not just an alternate return path.
* Given the above, a case can be made for moving it to the front of the declaration to highlight this significant difference and ensure it isn't missed when reading code (especially when reading quickly).
* It aligns with the vocabulary we naturally use: "an async function".  (The same can be said for "throwing function" of course, so this really hinges on the importance you place on the first point)

> -Chris

More information about the swift-evolution mailing list