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
.)