4 ms·
Honestly I have yet to see a language really nail async. For instance much attention has been given to async rust, but in my experience it is 100x more difficu
by skohan 5y ago
Honestly I have yet to see a language really nail async. For instance much attention has been given to async rust, but in my experience it is 100x more difficult to work with async than standard synchronous rust. So many gotchas, and the errors are extremely cryptic which is counter to what you expect in normal rust.
I found working with GCD was actually pretty good: of course it doesn't solve the safety concerns which rust aims to, but it's a sensible set of primitives which can be composed to achieve virtually whatever async problem you want.
I think there are two possibilities:
1. We haven't really landed on the right abstraction for async yet. Current approaches try to hide the wrong bits of complexity and this makes async difficult to reason about, or at least makes it a bit of a minefield of special cases and considerations.
2. Or, async programming is just complex, and there's never going to be a way to tie it up as a neat little consistent language feature.
- dnautics 5y ago> We haven't really landed on the right abstraction for async yet I think there are two 'correct' abstractions for async. One is where you build it as a high level abstraction on top of other concurrency primitives. Elixir's Task.async* functions do this spectacularly, and even do things that you don't get natively from Erlang that make it very nice to work with (by giving you an opinionated way of keying into global state partitions). The other "correct" async abstraction, imo, is a very thin, low-level abstraction over function frames and context switches. The zig programming language gets this right, IMO. I was able to write a feature that wraps zig's low-level async primitive in an event loop around the erlang's cooperative FFI scheduler, the rust equivalent hasn't figured that out yet, and since zig is colorless, you can use the same function in a non-async context, the yield points are just no-ops (which rust will never let you do, for reasons beyond just safety).
- fwip 5y agoHonestly, I think Go's approach of syntactically light green threads is just better than async-as-keyword. There's a lot of other unergonomic stuff (like the verbosity of channel definition) that can make it a pain to use, but that's more Go-the-language's fault. Everything a modern computer does has potential latency - whether that's waiting for the database to respond or waiting for L2 cache to respond. The programmer and the application needs decide when it's worth waiting around for the result, and when it's important to do other work in the meantime.
- kgeist 5y ago>Honestly I have yet to see a language really nail async What about Go?
- dnautics 5y agoGo has concurrency, but I would hesitate to call goroutines an async abstraction (it is a `spawn` abstraction) because it lacks a built-in way of returning the retval of the called function (no await); you must implement that yourself with channels. Last I checked, which to be fair was about 5 years ago, go did not give you a generic way to do this with the standard library either, probably because it... Requires generics.
- kgeist 5y agoThe equivalent of "async/await" in Go is a simple function call, not channels. As far as I understand, the main use of async/await in a web server is to work around blocking I/O calls. Node.js originally solved it with an explicit event loop/callbacks; C# added async/await which implements a state machine by bifurcating ("coloring") the entire API into sync and async functions (which, I think, is mistake); and in Go, I/O is asynchronous under the hood: the scheduler simply parks a goroutine in a blocking call and returns to it once it finishes, so your code looks like simple synchronous code but is actually asynchronous under the hood without any blocking. So there's simply no need for a special "await" keyword because all you need is a simple function call. If you want more complex scenarios (parallelize computation by spawning multiple goroutines, for example) -- that's when you need channels, but from my experience, it's rarely needed in the context of your typical web server. So, async/await is akin to cooperative multitasking where you have to explicitly tell the OS to yield, while in Go it's preemptive multitasking, which is done by the scheduler automatically and you don't even think about it. The downside is that it's not tunable like in C#, you have to trust the system's defaults. As for generics, they're going to be added in the next release of Go, scheduled for February 2022.