Having used async & await extensively in C# they're pretty great... but I can't help thinking we're explicitly declaring something that mostly could be implicit if the language was designed right.
For example: if I'm calling an IO method that returns a value it should be able to figure that out and orchestrate the code to run asynchronously and await for the value when it's referenced. If I have several asynchronous calls methods in a method it should be able to optimize it so they all run in parallel as optimized as possible.
It might need some added declarative syntax on methods but I'd rather prefer that and make the compiler smarter, instead of offloading that complexity on the programmer per default. Sure you should be able to explicitly control asynchronicity if needed but in most cases that doesn't seem optimal nor necessary
Adding async/await all over your code adds to the "crud" that makes code more verbose and harder to read and understand
The main purpose of the async/await keywords is to help the programmer reason about what can happen when.
Having calls be implicitly parallel makes it difficult for the programmer to predict what could be happening elsewhere in the program. E.g., this passage from the Oz documentation[1]:
"Oz 1, supports a fine-grained notion of concurrency where each statement can potentially be executed concurrently. This results in a fine-grained model similar to the actor model. A good exposition of the Oz 1 programming model is given in [Smo95]. Our experience using Oz 1 showed that this kind of model, while theoretically appealing, makes it very hard for the programmer to control the resources of his/her application. It is also very hard to debug programs and the object model becomes unnecessarily awkward."
For I/O in particular it is also difficult for the machine to know what is safe to do in parallel. For instance, two writes to the same file probably has to happen in order. What about writes to different files? What about HTTP requests? Most I/O has the potential to produce wrong results if rearranged in arbitrary order, and requiring the programmer to mark them sequential becomes a potential source of error and a cognitive burden.
In most cases it's better to be slow and correct by default, and allow the programmer to explicitly run things in parallel in the cases it matters.
I agree that those can be issues... but that can be solved by annotating the methods to those constraints. The compiler can even highlight potential problems and ask that you annotate them.
The benefit of this approach is that the compiler should be able to see more opportunities for optimization and also result in fewer bugs.
You need that "crud" so that you actually know what your code is doing. If the compiler automatically spins up another thread and finishes there while you return from that function without you knowing it would lead to all sorts of race conditions and other threading headaches. Making them explicit makes it clear what the code is doing.
that's not necessarily true, in some cases yes. But as I said, control could and should be there for the asking, I don't want a ORM style black box where god knows what happens behind the scenes... but I think you could declaratively markup methods so the compiler knows what is safe to do. For instance marking them as pure, mutex etc etc.
It's kinda the same thing except telling the compiler what to do you instruct it what's safe to do, and it can make smart decisions from that. If the compiler/environment detects any ambiguity or risk it might even query the programmer to declare his intent
> Having used async & await extensively in C# they're pretty great... but I can't help thinking we're explicitly declaring something that mostly could be implicit if the language was designed right.
Yes, basically, they are explicitly declaring dataflow dependencies. There are languages that handle this automatically, e.g., Oz [0].
OTOH, async/await is a solution that can be retrofitted onto an existing language that isn't fundamentally designed around dataflow dependencies without changing the semantics of existing code.
For example: if I'm calling an IO method that returns a value it should be able to figure that out and orchestrate the code to run asynchronously and await for the value when it's referenced. If I have several asynchronous calls methods in a method it should be able to optimize it so they all run in parallel as optimized as possible.
It might need some added declarative syntax on methods but I'd rather prefer that and make the compiler smarter, instead of offloading that complexity on the programmer per default. Sure you should be able to explicitly control asynchronicity if needed but in most cases that doesn't seem optimal nor necessary
Adding async/await all over your code adds to the "crud" that makes code more verbose and harder to read and understand