Remix.run Logo
NeutralForest 7 days ago

I thought that was interesting and I definitely get the frustration in some aspect. I'm mostly familiar with Python and the function "coloring" issue is so annoying as it forces you to have two APIs depending on async or not (look at SQLAlchemy for example). The ergonomics are bad in general and I don't really like having to deal with, for example, awaiting for a result that will be needed in a sync function.

That being said, some alternatives were mentioned (structured concurrency à la Go) but I'd like to hear about people in BEAM land (Elixir) and what they think about it. Though I understand that for system languages, handling concurrency through a VM is not an option.

the_mitsuhiko 7 days ago | parent | next [-]

> structured concurrency à la Go

Go does not have structured concurrency. Goroutines as far as I know don't have much of a relationship with each other at all.

NeutralForest 7 days ago | parent [-]

My bad, thanks for the correction.

toast0 3 days ago | parent | prev | next [-]

Sorry, I kind of spaced on reading the article, but from BEAM land, everything is built around concurrent processes with asynchronous messaging.

You can send a message to something else and wait for the response immediately if you want to write in a more blocking style. And you can write a function that does bot the sending a message and the waiting, so you don't really need to think about it if you don't want to. All I/O pretty much feels the same way, although you get into back pressure with some I/O where if there's a queue you can opt to fail immediately or block your process until the send fits in the queue.

The underlying reality is that your processes don't actually block, BEAM processes are essentialy green threads that are executed by a scheduler (which is an OS thread), so blocking things become yields, and the VM also checks if it should yield at every function call. BEAM is built around functional languages, so it lacks loops and looping is handled by recursive function calls, so a process must make a function call in a finite amount of code, and so BEAM's green threading is effectively pre-emptive.

The end result of all this is you can spawn as many processes as you like (i've operated systems with one process per client connection, and millions of client connections per node). And you can write most of your code like normal imperitive blocking code. Sometimes you do want to separate out sending messages and receiving responses, and you can easily do that too. This is way nicer than languages with async/await, IMHO; there's no trickyness where calling a blocking function from a async context breaks scheduling, and calling an async function from a non-async context may not be possible... You do still have the possibility of a function blocking when you didn't expect it to, but it will only block the process that called it and transitively, those processes that are waiting for messages from the now blocked process.

Java's Project Loom seems like it will get to a pretty similar place, eventually. But I've seen articles about some hurdles on the way; there's some things that still actually block a thread rather than being (magically) changed to yielding.

Again, IMHO, people didn't build async/await because it is good. They built it because threads were unavailable (Javascript) or to work around the inability to run as many threads as would make the code simple. If you could spawn a million OS threads without worrying about resource use, only constrained languages would have async/await. But OS threads are too heavy to spawn so many, and too heavy to regularly spawn and let die for ephemeral tasks.

vacuity 2 days ago | parent [-]

To offer a different perspective, green threads are far easier to use for programmers (which is a valuable quality; I'm not dismissing that), but async/await is the better abstraction if you know how to use it. I justify this more in [0]. Async/await is great, but pragmatically it should not be pushed as the sole "asynchronous programming" abstraction. It requires more QoL infrastructure to be even somewhat usable as general-purpose, although I think it'll be worthwhile to continue bridging the gap.

[0] https://news.ycombinator.com/item?id=41900215

lbrindze 7 days ago | parent | prev [-]

BEAM very much falls into the same camp as the author's description of Scratch does at the beginning of the article. You have a lot more granular control than Scratch, of course, but it also loosely follows the actor model