Hacker News new | ask | show | jobs
by pmoleri 1103 days ago
> How does "func EatADonut() async {}" aka "eating a donut is an inherently async action" even make sense to people?

Of course it does, read it as "beware, something blocking down the road".

If you can EatADonout without blocking, please do, but want it or not that's a different implementation, one that doesn't block and the signature it's telling you so.

We're so used to sync and having hidden blocking operations. I wonder if in an alternate universe the first languages considered the blocking/async nature of operations and then some newer languages considered hiding this information into seemingly sync functions would produce similar but opposite outrage against it.

1 comments

> want it or not that's a different implementation,

Implementation is the same. In both cases it's the same set of CPU instructions, but async/await languages create artifical division, forcing developer to think othervise.

So, let me explian my reasoning. Code starts with a developer's mental model of a problem and behavior of the system and then translating it into the code. The more straightforward this translation, the more readable the code. Code is a second degree map of problem domain so to speak ("real" world -> mental model -> code).

Like if you want to add two values, the simplest form of code would be "add(2,2)" which is pretty straightforward. If the code forces you to do some mental gymnastics (i.e. "2 2 addOnlyEvenNumbers") - that's less straightforward, less clear and less readable.

In the same vein, if you want to execute some function ("EatADonut" or "MakeHTTPCall") – you may care or not care about blocking and waiting for results. But it's your call. So it makes sense to give you two options to run this function. Go has simplest possible solution – "eatADonut()" vs "go eatADonut()". It doesn't matter what is a "default" here – it could be "eatADonut()" (go to background) vs "sync eatADount()". What matters that "eating a donut" is just a set of instructions to the CPU, and it's up to caller to decide how you want to execute it in terms of concurrency.

Now, "async/await" approach turned this ownership of "synchronicity" around. Now function is deciding how it should be called. Mental model of "actions" now needs to be translated into "actions being async or sync for the purpose of fitting into this language concept". Which is cognitively expensive for no added benefit.

Sure you can rationalize it, and get used to it as to any other absurd design, but it still adds unneeded complexity to the code, makes it less readable and less clear.

>In both cases it's the same set of CPU instructions

It's actually a very different set of CPU instructions. the function EatADonut is the same set, but "async" means the kernel needs to take time out of its execution to do some action as small as accessing an open thread, or as large as "gain access into a completely different piece of hardware" before putting that set of instructions onto that different thread. Not the program, the actual scheduling process between your program and the OS you are executing on. Then it needs a way to to get that result and sync it back onto whatever thread spawned it and access the result.

It is in fact a huge action, so marking it with Async is basically a very explicit warning.

>Which is cognitively expensive for no added benefit.

On the contrary, I can't even begin to imagine the amount of compiler optimizations it saves on as well to have that be explicit in code. I'm sure Go has to do all that on the fly while a colored language gets to allocate all those potential processes before the program runs. It's only no added benefit if you dont care about performance. But to be frank, you probably don't need more than a single thread if your problem isn't bounded by perormance. Parallel programming is all about getting something done faster after all.