The ErrorPropogation operator should be generalized as a monadic continuation operator for other single parameter type constructors such as Async<T>. Ideally, the ? operator should be trait defined where Option<T>, Result<T>, and Async<T> would have core implementations but other libraries may make implementation for their own types. Additionally, an implementation for composition provided by core would be nice: f(g(h(t))); may then also be expressed as, f?g?h(t);
A monadic discussion (single parameter type constructors) simplifies that discussion a bit and does place things within an existing framework. However, if the ? symbol will not be available then perhaps another might be suitable, such as the following: ??, !!, or $.
Interesting; becoming generic over modifiers like const and async. Yet, in the same way const become available to runtime context why can't async have a smooth fallback to blocking when invoked from outside an Async<T> context? Either the Async<T> function detects its context at runtime or the compiler determines it to generate an appropriate version. Nonetheless, cool.
With respect to my post, I believe providing a monadic context is a separate and valuable thing. It is already effectively what ? provides at the moment especially with respect to Option<T>. Result<T> is a little more hand-wavy since it's really a Result<T,E> but the E is fixed. I see this enhancement as generalizing an existing feature and not so much as adding a new one.
The comparison doesn't hold because const restricts what the function can do while async extends it. A more proper comparison would allowing to call non-const functions in const functions, which is not possible.
Also, you need to define what's considered "smooth" here, and I'm sure it will either be impossible/very hard to implement or many people will disagree with you. Remember that async should still be available on no_std platforms with limited resources.
I wonder if the scenarios where an asynchronous function can be systematically converted into a synchronous function, such as with blocking, are common? Asynchronous functions are already quasi synchronous in form and the extrapolation doesn't seem extreme. Instead of doing a wait, for example, a simple block would suffice within a synchronous context. Of course, if the wait is dependent on the external asynchronous activity this approach would not work in those cases. The question then becomes, is the absence or even presence of such dependencies detectable at compile time or even runtime? Can the compiler accommodate the use of an asynchronous function within a synchronous context with a wrapper functions?
Use of non-blocking syscalls. How is the compiler supposed to de-async a call to epoll? signalfd usage? userfaultfd?
There may be other flags involved making things work. O_NONBLOCK or the like come to mind.
How do you propose do de-async anything using inherently non-blocking APIs like io_uring or IOCP?
Anything using timers needs some coordinated runtime support (unless using a platform api such as timerfd).
I suppose the compiler could set up a runtime and spawn it manually…but what runtime? At that point, why doesn't the "explicit is better than implicit" guideline take precedence?