It's maybe interesting to note that the `async` library in use here is very simple and easy to understand. Nearly every function is one or two lines. Likewise `TQueue` is extremely simple (and easy to prove correct) thanks to STM, and also generally has good performance.
A lot of the complexity here is just hidden in Haskell's runtime, which implements async processing based on green threads, besides other features such as GC. Though to be fair, the software transactional memory (STM) featureset is quite unique to Haskell since it relies on the availability of pure functions to ensure correctness. It's kind of hard to imagine a full equivalent to it in other well-known languages.
Concurrency in Haskell: Fast, Simple, Correct
51–60 of 129 posts
Re: Concurrency in Haskell: Fast, Simple, Correct
#52Rust has a bunch of these while being maintainable.
Isn't concurrency in Rust a notorious pain point? Or am I confusing it with async which is different? [I am stuck in an era before parallelism, so I don't really understand these things]
Re: Concurrency in Haskell: Fast, Simple, Correct
#53Earlier quoted context omitted.
Isn’t that one of Dijkstra’s supposed comments?
It's Knuth! [1] "Beware of bugs in the above code; I have only proved it correct, not tried it." [1]: https://www-cs-faculty.stanford.edu/~knuth/faq.html
Re: Concurrency in Haskell: Fast, Simple, Correct
#54Is it because it is just a very hard thing, or is it because its a synchronous language, with async bolted on? (I'm talking about a purely language point of view, not from a python VM / GIL point of view)
Re: Concurrency in Haskell: Fast, Simple, Correct
#55I love Haskell because I can write provably correct code that still doesn’t work
Re: Concurrency in Haskell: Fast, Simple, Correct
#56Earlier quoted context omitted.
You obviously haven’t ran anything on the BEAM (Erlang’s VM).
Correct. Erlang also uses green threads?
When data is immutable, it can be freely shared. Changes to the data essentially uses copy-on-write. And it only writes the delta change, since you don't need a deep copy due to immutability. Add that the garbage collectors of Haskell and Erlang are designed to work with a high allocation rate and have 0 cost for dead data, and this is much faster than what people think.
The way you implement a webserver in either Haskell or Erlang is rather trivial. Whenever there's an incoming request, you make a thread to handle it. So you don't have 1 webserver serving 10k requests. You have 10k webservers serving 1 request each. And since they are started from the same core data, they'll share that due to immutability. See also old-style Apache or PHP and fork().
Re: Concurrency in Haskell: Fast, Simple, Correct
#57> It gets weirder: in Haskell, exceptions can be thrown to other threads!
What's really interesting is that because of purity, you have to have asynchronous exceptions otherwise you give up a lot of modularity. At least that's what Simons Marlow and Peyton Jones argue in Asynchronous Exceptions in Haskell (2006): https://www.microsoft.com/en-us/research/wp-content/uploads/...
> While the semi-asynchronous approach avoids breaking synchronization abstractions, it is non-modular in that the target code must be written to use the signalling mechanism. Worse still (for us), the semi-asynchronous approach is simply incompatible with a purely-functional language, such as Concurrent Haskell. The problem is that polling a global flag is not a functional operation, yet in a Concurrent Haskell program, most of the time is spent in purely-functional code. On the other hand, since there is absolutely no problem with abandoning a purely-functional computation at any point, asynchronous exceptions are safe in a functional setting. In short, in a functional setting, fully-asynchronous exceptions are both necessary and safe — whereas in an imperative context fully-asynchronous exceptions are not the only solution and are unsafe.
If you can read PLTese, it's really quite a nice paper.
Re: Concurrency in Haskell: Fast, Simple, Correct
#58Earlier quoted context omitted.
No reason. OC probably thinks that immutable data structures are always copied when being operated on.
Yes indeed, unless you use ropes or other specialised structures
Tries (like scala’s Vector) or trie maps (the core map types of Scala, Clojure and probably Haskell?) aren’t copied on updates.
In fact, whether a data structure is an immutable or persistent data structure or merely an unmodifiable data structure (like Kotlin uses) is based on whether it requires full copies on most updates or not. In FP languages, immutable data structures aren’t “specialized” at all.
Re: Concurrency in Haskell: Fast, Simple, Correct
#59I’m not familiar with Haskell concurrency. The combination of green threads and large memory allocations due to immutable data structures sounds like it would be hard to implement a web server handling 10k+ concurrent requests on commodity hardware? Btw. too bad author talks about microsecond guarantees usage but does not provide a link, that would be interesting reading.
Re: Concurrency in Haskell: Fast, Simple, Correct
#60I thought it was a bit odd that the author claims there’s no mutexes in sight, the TVar is effectively a mutex guard unless I’m misunderstanding this? (I’ve written exactly 0 lines of Haskel). Or is the claim that the lack of ceremony and accidental complexity around threading is the real win for concurrency here?
No, a TVar is not a mutex guard. A TVar is a software transactional memory (STM) variable. STM works just like a database: you batch together a sequence of operations into a transaction and then execute them. During execution of a transaction, all changes made to the contents of the TVar are stored in a transaction log. If some other transaction occurs during the execution then the whole thing is aborted and re-run.…
I know you say it depends on how much contention one sees but I'm interested in the performance hit. Also, is STM the "standard" (or accepted) way to do async in Haskell?