On the proliferation of try (and, soon, await)

That's true, but to be totally fair, there's a difference with async. Assuming you are able to reason about which code has access to a given class instance, it's possible to reason that a synchronous call into code where the partially-modified instance is inaccessible will not cause the instance to be re-entered and thus observed in the partially-modified state. When the call is async, that instance is open to access, not just by the callee, but by any currently-suspended code that may resume at the point of the call.

That said, you could argue that being able to reason about which code has access to a given class instance is a fantasy that's rarely fulfilled in reality… another good reason to eschew classes :wink:

For a programmer, the interesting thing is, “Does this call initiate some concurrent task, which will execute in parallel with the current codepath, and if so how can I interact with it (observe progress, cancel, get notified when it completes, etc.)?”

Just trying to get a grip on what you're saying here… IIUC, every async call (except those into the same actor?) is a suspension point. Doesn't that mean that each one can effectively initiate a concurrent task, since some waiting task might start when this one suspends? Also, I'm not sure about “in parallel,” if we're distinguishing parallelism and concurrency. If some other thread spawns a new thread at the moment I make a call, has that call effectively initiated some task that will execute in parallel with the current codepath?

It sounds like you're talking about initiating a task that persists past the call, to which the caller can get access… which sounds like a future to me.

All this other talk of suspension points is important to the implementation, but doesn’t directly affect the surface-level of the language. You always have to wait for a call to complete before the next line is executed, so why should some calls require an await keyword?

Well, yes, that is the question I'm putting on the table.

The new async let , however, is different. It actually introduces asynchronous execution. It brings concurrency into the language. And when you have an async let, then it makes sense to await it when you need the value.

OK, but the fact that the word await “makes sense” at that point in the code is not a good enough reason for the compiler to mandate it. It has to serve some purpose, alerting the reader of the code to… something. For example, & is mandated on (nearly all) inout arguments in order to alert the reader to mutation, which materially affects the reader's ability to understand the meaning of the code.

I argue that there's nothing about evaluating an async let that makes it more worthy of a mandated keyword than any other async call. Both have the same potential to allow re-entrant access to shared state that wouldn't be allowed if the call were not async. But since that issue seems increasingly marginal as actor isolation becomes stronger, it's not clear to me that it's worth the cost.