SE-0317: Async Let

Well, in practice, this is just an asynchronous program (not concurrent; no tasks are created):

func f() async -> T { await E }
let x: T = await f()
G // "some other stuff"

and E will always evaluate before "some other stuff". But this is a concurrent binding:

// inline f, move it's effects, 'async', in front of let
async let x: T = await E  // might be a warning that `await` not needed
G // "some other stuff"

so "some other stuff" now can happen before E completes. Of course, if you actually are required to use the bound value of an async-let at least once, then this non-equivalence becomes a bit more clear when you are forced to write await around the access to x in the second version, but not the first.

Otherwise, it's hard to speculate how folks might become confused, so I can't think of a better answer. When we were discussing effectful properties, the design was centered around putting effects specifiers close to the accessor, not in front of the let. The reasons for why I avoided putting async or throws in front of a var doesn't really apply here:

Since async-let is meant to be a short-hand, it doesn't really make sense to have an accessor like effectful properties do. Requiring the right-side of the binding to be a closure () async -> T and then the variables type is just T was also thought of early on, but it turned out to be unpopular, I think.

1 Like