> Another issue would be a coherent concurrency system. Today we have a mix of gcd and actor system, declarative task, async await, etc which when put together makes me totally unable to understand what's going on in a real world program, unless you're doing like i do in my team : i specifically ban all modern concurrency apis in favor of the old ones.
There’s a layering:
- gcd is the lowest level
- async/await lets you write tasks with suspension points (they wait for events to happen); but an async function is still conceptually executed serially
- the Task API let’s you spin up async functions into various arrangements of parallel tasks and wait for those tasks to complete
- ‘async let’ is sugar for creating a Task to compute a single expression
- for async functions and Tasks to share data there is a Sendable model and various static isolation checks
- actors build on everything else; actors are classes whose state is completely isolated to the actor, and all method calls are posted on the actor’s executor
Memory-safe concurrency is hard. There’s a lot of ground to cover to replace the kind of things people do with shared state concurrency, and just actors alone are not enough.
> Compare that to go, where there's (at most) one satisfying way to design the problem in the typesystem. As a sideeffect people have to think more about the problem they're trying to solve, and how to simplify it to the maximum.
I like the aesthetics of small languages too, but I think Go picked the wrong small subset. It reminds me of Java in the 1.x days, for x <= 4, where the language had fundamental expressiveness constraints that created a huge amount of unavoidable boilerplate and type unsafety in even the most basic programming situations.