Actor Model fundamentals compromised?

As nomeata pointed out, there are multiple layers at play here.

On one level, we have a simple form of actor (mapping to IC canisters). An actor communicates by message passing as you would expect, messages are received atomically, they can send an arbitrary number of follow-up messages without blocking on responses.

But in its raw form, this model is rather awkward to use in practice. If you have a sequence of messages and responses that you need to process in an interaction, then you’ll be forced to manually reify all the intermediate state (and control flow, including call chains) somehow and store it somewhere. This can be tedious and error-prone, and in the general case, amounts to a manual CPS-transformation of the entire program.

Async/await is best considered a layer of “syntactic sugar” on top of that, which does this transformation for you. An async function with awaits should not be thought of as the implementation of a single message, but an interaction involving a sequence of messages and responses, separated by await points.

And yes, this has the risk of luring you into a false sense of safety, where you forget that atomicity only extends to the next await. But that’s a trade-off that seems worth taking for the practical benefits.

In fact, we have been discussing ways in which the compiler or the type system could help detect erroneous state dependencies that cross await boundaries, and where the programmer would need to be explicit if they want to opt in into such a dependency. But that’s a tricky design space, and we do not have a satisfactory solution yet.

7 Likes