> This was bad enough that Node.js eventually changed unhandled rejections from a warning to a process crash, and browsers added unhandledrejection events. A feature designed to improve error handling managed to create an entirely new class of silent failures that didn’t exist with callbacks.
Java has this too.
Surely by section 7 well be talking (or have talked) about effect systems
I like async and await.
I understand that some devs don’t want to learn async programming. It’s unintuitive and hard to learn.
On the other hand I feel like saying “go bloody learn async, it’s awesome and massively rewarding”.
>And some languages learned the right lessons from the coloring problem. For example, Go deliberately chose goroutines over async/await, accepting a heavier runtime in exchange for no function coloring at all. Java’s Project Loom (virtual threads in Java 21) made the same bet: lightweight threads that look and behave like regular threads, so no code needs to change color. The Loom team explicitly cited function coloring as a problem they wanted to avoid.
>Zig went further: it removed its compiler-level async/await entirely and rebuilt around an Io interface parameter that i/o operations accept. [...] Though some argue that the Io parameter itself is a form of coloring.
>Language designers who studied the async/await experience in other ecosystems concluded that the costs of function coloring outweigh the benefits and chose different paths.
Green threads with a heavier runtime isn't the "right" lesson. Instead, it's a fundamental tradeoff of no-function-colors-ergonomics vs maximum performance. Some simply don't want to pay the "performance tax" or "interoperability tax" to avoid function coloring.
The following is copy-paste of previous discussion links and added Wayback Machine links because lobste.rs is often down. Anyone who thinks Golang made the "right" decision should read why early Rust developers tried green threads and abandoned them. The revised conclusion is that Go's design is a tradeoff that works for their devs but not for Rust. The author of this thread's article does not mention the issues the Rust devs were highlighting.
>Golang and Erlang successfully employ preemptive M:N models and there are no coloring problems in those languages.
The designer of Rust async/await says Go's green threads approach has an unavoidable performance tradeoff: https://news.ycombinator.com/item?id=37439886
Basically, Rust has lower-level performance constraints than Go that they didn't want to compromise on. (I.e. performance constraints of FFI C-interopt with predictable fixed stack space.) :
- https://lobste.rs/s/bfsxsl/ocaml_4_03_will_if_all_goes_well_... (https://web.archive.org/web/20250815150638/https://lobste.rs...)
- https://lobste.rs/s/y3fsrm/what_is_zig_s_colorblind_async_aw... (https://web.archive.org/web/20250402055806/https://lobste.rs...)
- https://lobste.rs/s/eppfav/why_i_rewrote_my_rust_keyboard_fi... (https://web.archive.org/web/20250412170821/https://lobste.rs...)
- https://news.ycombinator.com/item?id=21475154
As counterpoint, Rust's original designer, Graydon Hoare, prefers "green threads". In a recent blog post (2023-June), he mentioned that he understands why the Rust team got rid of it for performance reasons but he's not fully convinced of the ultimate tradeoff:
-> Async/await. I wanted a standard green-thread runtime with growable stacks -- essentially just "coroutines that escape, when you need them too". Possibly with a somewhat-embeddable outer event loop / IO manager library, but that's always going to be a little tricky. Go started and stayed here, but they had to do a bunch of gory compromises to make the FFI work and it leaves a lot of performance on the table and torpedoes a bunch of embedding opportunities. Rust started here too, and it got rewritten a couple times and eventually thrown out because of a lot of reasons but none which completely obviated the need (as evidenced by the regrowth of Async/Await and Tokio). I've softened my position on this and have a grudging respect for where Rust wound up (especially in enabling heterogeneous selects, which I think puts it in a similar and enviable expressivity class as Concurrent ML). But if I'm being honest I never would have agreed to go in this direction "if I was BDFL" -- I never would have imagined it could even work -- and still don't know that I think the result quite pays for itself. -- from https://graydon2.dreamwidth.org/307291.html
Function colouring, deadlocks, silent exception swallowing, &c aren’t introduced by the higher levels, they are present in the earlier techniques too.