Many languages have a distinction between statements and expressions, and some language designers thought that was ugly and designed languages where everything is an expression.
But because of how people think, an application typically has a rough hierarchy to it by the designer's intent. So the fact that this becomes a bit more explicit because a language marks statements or async functions as having a specific place in that hierarchy isn't generally a bad thing.
(1) either you write two versions of almost every function or you can only support one use case (sync or async). If you could write generic functions which work in both cases it would be better.
(2) it creates a lot of noise writing async/await. It's like if we had to always write x = call f() in the sync case.
Assuming it's possible, one way is to create a new event loop in a thread. This means you're creating and tearing down threads for a "trivial" wrapper. And, really, because threads are expensive, you'd probably want to let the caller provide an event loop.
Really, you wind up asking the caller to tell you what event loop to use. Let's compare real async vs. the sync wrapper function:
async func main() {
await middle()
}
async func middle() {
await libraryFunc()
}
# Using the sync wrapper without hiding the loop.
func main() {
loop = getLoop()
middle(loop)
}
func middle(loop) {
libraryFuncWrapper(loop)
}
You wind up "coloring" your functions through a parameter instead of by marking them "async".Perhaps your async implementation lets you use a globally defined event loop, but, really, globals are just backdoor parameters.
None of that means that such a wrapper is never going to be a good solution. Grabbing the global event loop probably works for many use cases. It's just that it doesn't avoid the coloring problem, it only hides it.
It would take a couple months of bike shedding syntax and discussing internals but I feel like there's a version of this (applied to other function colors) that allowed a library author to annotate/decorate a function for an inclusive or exclusive set of these decorations the compiler can pick to call from a calling context, and allow the function author to specialize over the combination of decorators that actually get called. Since the set of decorators is small (I can only think of 2-3, including async) this feature would be straightforward to implement albeit potentially verbose.
There's a fancy compiler pass to make async work though. You need a special control flow analysis to figure out yield points in async bodies, since this new syntax would have no await.
It's probably too late for that. It might be implemented in terms of HKT.