4 ms·
> It's just making things needlessly hard for programmers in an attempt to save effort from computers. Well actually it is supposed to make concurrency easier
by jsiepkes 5y ago
> It's just making things needlessly hard for programmers in an attempt to save effort from computers.
Well actually it is supposed to make concurrency easier for programmers.
What would you say is an "easy" way to deal with concurrency? Handle threading, memory visibility issues, locking, etc yourself? Doing it "reactive"? I guess personally I too would say something messaging based such as the actor pattern but not everything is a good fit for that model.
Async is intended to be a more generic (ie. fit for everything) and easy way to program. I'm not saying it succeeds in that but the goal wasn't to save the computer the effort.
- masklinn 5y ago> What would you say is an "easy" way to deal with concurrency? The Beam/Erlang/Elixir model. It's a bit more constraining but it's just so clean and reliable, and processes having individual mailboxes makes it much simpler than having to wonder what synchronisation primitives to use, then decide how to share them (globals? parameters? others?). Just get a reference to a process and you can send it messages, simple. If you want replies, send it your address. Also simple. And matches the real world very, very well (at least the real world of a few years back when you'd send letters through the mail, electronic communication channels tend to be more full-duplex).
- jsiepkes 5y ago> The Beam/Erlang/Elixir model. It's a bit more constraining but it's just so clean and reliable [..] I agree. But I think that only works if you embed it in the language like Erlang did. If you make it optional you get libraries which use different models, people still making concurrency mistakes, etc. But for more generic languages then Erlang such as Rust (or Java, C#, etc.) this simply isn't an option. Because the actor pattern is too opinionated to be a fix for all.
- fulafel 5y agoWe know how to do interiperable DSLs for IO, see eg IO monads in Haskell, or ebpf in Linux. And Erlang could adapted for embedding, it's already designed to isolate its processes in a vm out of the box, so wouldn't leak back to the host language...
- akvadrako 5y agoAs he mentioned Erlang he probably finds the actor model easier. Threads and queues are pretty easy to reason about too — just avoid locks and shared memory.
- _flux 5y agoWhen async is visible in the type system it means you need to write things like iterating containers twice: once for dealing with non-async values, and a second time for handling async operations. Well maybe this isn't the best example because you already have iterators for the plain iteration part; however let's say you wanted to make an iterator for a container that needs async to iterate the data, then you find yourself having the same problem that the existing facilities making use of the old "non-async" iterators become useless. Alternatively you let async leak into _everything_, even if they are not actually using async themselves. There's nothing wrong with runtime-driven schedulers handling the threads for you, but async isn't the only way to do it. But it is one of the easy ways to do it and a way that is implementable as a library. In particular single-threaded async (javascript) can be painful because you need to be mindful about not doing anything for "too long" without splitting the work into several parts. In a sense with async we're back in the era of co-operating scheduling. Even in multi-threaded environment is can be based on luck or faith that not all the threads allocated happen to get stuck on long-running jobs when short and fast user-facing interactive async tasks are starving. (A problem solved by over-allocating threads of course.)
- valenterry 5y ago> When async is visible in the type system it means you need to write things like iterating containers twice It should be noted that this is only if the type-system in your language is unable to properly abstract over containers and their capabilities. If it is powerful enough however, then you can write the code once, under the (important!) assumption of short-circuiting in the case of errors.
- _flux 5y agoSure, it's possible to parametrize your algorithms. However, do you need to explicitly parametrize them or does the language do it for you? I'm not aware of any language that would remove the need to consider them and just automatically "work" with async types whereas "normal" threading—with errors handled via exceptions—more or less work out of the box the way you expect.
- kaba0 5y agoHow does async prevent anything about memory visibility and locking issues? Afaik only immutability and borrow-checker-like solutions can prevent data races (but not race conditions!).
- lelanthran 5y ago> What would you say is an "easy" way to deal with concurrency? Handle threading, memory visibility issues, locking, etc yourself? Message queues, with workers atomically reading messages of a single queue, and placing responses on another queue. Sure, it's not as flexible as starting threads[1], but 99 times out of a 100 you can use message queues and there's literally no race condition to happen, no memory visibility issues, no locking, etc. [1] Some solutions, such as those that games employ, are rarely able to implement concurrency with just message queues, but to be honest if your problem is such that you can only employ solutions that require global state to be modified by several threads, then Rust, and similar 'safe' languages aren't usable there either.