Live data from Hacker News

Concurrency in Haskell: Fast, Simple, Correct

bitbashing.io

51–60 of 129 posts

Re: Concurrency in Haskell: Fast, Simple, Correct

#51

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.

Quibble: Both Clojure and Scala have a Software Transactional Memory implementation, and the original Clojure Ant demo showed this.

Re: Concurrency in Haskell: Fast, Simple, Correct

#52

Rust 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]

This why I haven't fully embraced Rust yet. Whenever I ask about safely mutating shared state (see sibling comment), I'm met with silence, or some comment like: Rust guarantees that you aren't mutating shared state.

Re: Concurrency in Haskell: Fast, Simple, Correct

#53
post #45

Earlier 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

It's incredible that given how fuzzy and inaccurate human memory is, we treat any LLM that can't perfectly recite volumes of information as somehow beneath us.

Re: Concurrency in Haskell: Fast, Simple, Correct

#54
I don't know how async is in other languages but I find Pythons async incredibly difficult to use, and I kinda feel validated about how poor chatGPT is at it as well.

Is 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

#56

Earlier quoted context omitted.

You obviously haven’t ran anything on the BEAM (Erlang’s VM).

Correct. Erlang also uses green threads?

Yes. And immutable data structures.

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
From the footnotes:

> 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

#58

Earlier 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

Lists aren’t copied on prepend.

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

#59

I’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.

The interaction of laziness and purity means that the memory costs are not always what you think. Purity means that it's a lot safer to share structure between old and new versions of a data structure where an imperative language would have to do defensive copying, and laziness means that you can incrementally amortise the cost of expensive rebalancing operations (Okasaki is the standard reference for this).

Re: Concurrency in Haskell: Fast, Simple, Correct

#60
post #8

I 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.…

> How it performs is another matter!

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?

Post reply on HN