>I hope your opinion about C#'s task system has improved since the last time[0], given what Gleam (and, in many ways, Elixir) does looks practically identical :)
Well no, not really I'm afraid. My reservation has always been with the codebase birfircation per my previous post. Gleam/BEAM languages, Go, and now Java, don't have async and sync functions. They have one kind of function which can be called either synchronously or asynchronously. The difference is in who decides: the function caller or function implementer. That a sync function can't call an async one amplifies the problem.
I know you dislike the "coloured function" metaphor [0] but for me it's a significant issue. I look at lots of C# and Python code, and see libraries now encumbered with both sync and async function variants (e.g. [1] [2]). That, to me, is a significant downside to async/await as implemented in those languages.
The Gleam example has all the convenience and readability of its C#/Python counterpart - but without the downsides.
Does anyone actually do anything other than immediately await the async thing? But all callers need to wrap everything in Task<> and awaits and async and whatnot...
If you want to do some parallel processing on a collection I'm sure we could find a way to do that instead of adding all the clutter we have now
> The Gleam example has all the convenience and readability of its C#/Python counterpart - but without the downsides.
This was mentioned in the write-up, but the big downside is interop. Green threads have significant downside when going across OS threads.
This is the same reason why Rust ended up with async. Async is basically the cost you pay for C interop. However, C# runtime-async will likely be much simpler than Rust async since ownership is GC-managed and doesn't need to be transferred across threads.
All that said, I'm also not convinced the codebase bifurcation is a bad thing. Async ~= I/O. As a regular C# user, I'm not particularly unhappy about splitting my app into "I/O things" and "not I/O" things.
Assuming it is unlikely Gleam does something that makes it outperform Erlang, which it compiles to. Now, it comes down to just how BEAM VM is, but nonetheless an argument - C# is a close to the metal programming language where your choices directly affect what happens under the hood, including the use of async/await. For some it may be an undesirable trait but it's precisely what makes it so fast, in domains where languages that are "more abstracted away" used to historically struggle and continue to have lower performance ceiling.
As usual, the "coloring" point misses the patterns that async/await enables, and that it is in many ways an "I/O Monad". Still, mixing faux-concept "differently colored threads" in .NET does not come with the same degree of pain it does in Rust or elsewhere (and there are good reasons for that).
You can block threads if you have to (which includes synchronously waiting for tasks), and Threadpool is desiged to deal with that appropriately and increase/decrease worker threads count to maintain optimal throughput. You just don't have to pay for it always, and as multitude of alternate implementations suggest there is no free lunch, as usual.
Also, sync vs non-async overloads, as I previously discussed, often do I/O in a completely different way. Other languages sell it with loud buzzwords like "NIO", while .NET keeps it boring - the workload will scale without your explicit effort, never throttling independent execution flows.
I continue to be convinced of the sheer degree of harm done by "that one article", and applying C# in practice cures this perception once you stop worrying and love the easy concurrency and parallelism that come with it.
you've made the performance argument before and I don't refute that C# shows up better in benchmarks than BEAM languages. Or many others for that matter (including Python).
The reductive argument there is that if performance is the sole priority then write machine code. That's extreme. A more robust one is that, according to the same benchmarks you reference, Rust is meaningfully faster than C# and C faster still. So if performance is the overriding objective then use one of those.
You'll justifiably push back on that and raise other factors in favour of .Net. And that's the point: it's about trade-offs and preferences.
For the apps I've built and been involved with, real world performance has been within commercial tolerance using languages that, at least according to benchmarks, are slower than the top performers. In teams of moderate size and above, managing codebase size and evolution is usually a bigger challenge. Requiring sync and async variants of functions detracts from that: not to mention the overhead of ensuring some level of consistency in when to use each form.
> I continue to be convinced of the sheer degree of harm done by "that one article"
We'll just have to disagree agreeably on that one. I see the coloured function metaphor as an elegant articulation of an important limitation, and it has served the community well in describing the problem.
> applying C# in practice cures this perception once you stop worrying and love the easy concurrency and parallelism that come with it.
Another disagree agreeably. In isolation yes, but with a non-trivial cost in bifurcation and the need for async/sync variants.
The claim was not that the performance is absolute. Instead, I'm poking at the assertion that Gleam's implementation does not have downsides, which is rather silly in the context of our discussion, is it not? (also async in C# and in Python are very different)
Not to mention, in the original reply this was raised as a discussion of implicit vs explicit suspend points as means to asynchrony and M:N threading and their trade-offs. Instead, you felt like reframing this as purely Language A vs Language B. I too am guilty of this, but I try to do better. In either case it tends to be less productive and derails the discussion, and is just not very nice.
On the "bifurcation of sync/async in .NET" question which seems to be what the argument in its confusion revolves around, I have written a long-form post and extracted it into a gist to avoid polluting the discussion: https://gist.github.com/neon-sunset/640a38f9f2af73ad888cb5b0...
Still, this subject deserves a better, proper, much more information-dense overview, ideally accessible to people unfamiliar with details of either async/await and tasks/futures or implicit suspend, how they relate to implementation strategies available to each of them, etc.
Unfortunately, I only have so much time and can spend so much effort on this, nor am sure whether there's value in that - I'm getting an impression that these replies come from a place focused on just seeking to confirm their point of view and signing boring praises to how Erlang and its derivatives is the one and only approach rather than understanding what drives different design decisions for achieving concurrency/parallelism across programming languages.
Also a big reason why C++ co-routines are the way they are, is that they were originally modeled in C# async/await as per Microsoft design in C++/CX before submission to the WG21 process.
With the big difference that all those magic classes, that also exist in a similar form in .NET, do have support in Windows Concurrency Runtime, and later C++/WinRT.
However since WG21 left the runtime part as exercise for the reader, we have the current mess of C++ co-routine talks at each conference, and even so, not everyone gets them.
Well no, not really I'm afraid. My reservation has always been with the codebase birfircation per my previous post. Gleam/BEAM languages, Go, and now Java, don't have async and sync functions. They have one kind of function which can be called either synchronously or asynchronously. The difference is in who decides: the function caller or function implementer. That a sync function can't call an async one amplifies the problem.
I know you dislike the "coloured function" metaphor [0] but for me it's a significant issue. I look at lots of C# and Python code, and see libraries now encumbered with both sync and async function variants (e.g. [1] [2]). That, to me, is a significant downside to async/await as implemented in those languages.
The Gleam example has all the convenience and readability of its C#/Python counterpart - but without the downsides.
I don't doubt you'll disagree :).
[0] https://news.ycombinator.com/item?id=39497110
[1] https://learn.microsoft.com/en-us/dotnet/api/system.io.strin...
[2] https://fastapi.tiangolo.com/async/