On why await shouldn't be a method

I would agree that most do not have to be aware of the transformation happening here. However, I also think that await is fundamentally different from a function call. By pretending await is a function call, you are confusing two very different operations: calling a function, and awaiting a future. Inside an async fn, there are some similarities between await and a function call, but there is still a fundamental difference between foo(x) and await bar: the former cannot block, and the latter cannot take arguments. Abstracting over arguments and blocking are two entirely orthogonal concepts, realized by functions and futures, respectively. So, even on that abstract level, clearly await is not a function call. (I very specifically did not write await bar(); that is a combination of a function call and an await, but we are only talking about the await part here.)

Admittedly, there is some overlap between function calls and async, namely both "trigger computation elsewhere" (they are both thunks or generalizations thereof). A thunk is basically the same as a function with no arguments, and async also involves executing a thunk, which is why I think some people argue that async is like a call. I disagree, the fact that some functions are just thunks and that async involves a thunk is not enough to conflate these two concepts.

If this discussion would happen in a Haskell context, await would basically be the bind of a monad -- and never would people in Haskell suggest that x <- bar; is the same as let x = foo in. There's a huge difference between a regular function call and the monadic bind action.

This is in fact very similar to ?, which also is a monadic bind operation. Are you saying ? is just a function call, too? We should maybe write it foo.try() then.

(The parallel with monads is also why I think the "parallel" with unsafe fn is inaccurate: unsafe is not an effect, there is no monad here, unsafe fn and fn are two different types for basically "the same" kind of object but with different semantic promises attached to them. They even have the same run-time representation. This is very different from fn vs async fn.)

11 Likes