Hacker News new | past | comments | ask | show | jobs | submit login

Nice write-up. Haskell and GHC have definitely been enormously successful platforms for PL research in general, and research in parallelism in particular. However, one unfortunate side effect of all that research effort is that there are now at least half a dozen libraries for parallelism and concurrency (STM, Async, Control.Parallel.Strategies, etc), to the point that it is hard for a software engineer to grasp the tradeoffs presented by the various options. I feel like I succumb to decision paralysis when I attempt parallel programming in Haskell; from a purely pragmatic perspective, I much prefer the Go approach, where there is a single canonical concurrency mechanism which is baked right into the language and is extremely well-documented.



Been a while since I've done Haskell, but the essential breakdown as I understand it is this:

At lowest level you have the Control.Concurrent primitives: forkIO and MVar. forkIO allows you to spin up new Haskell threads (not OS threads) and MVar's can be used to communicate between them. MVar's can be empty or full and block when the operation can't be completed immediately (writing to a full MVar or reading from an empty MVar). They also have single wakeup semantics, so if you have multiple readers blocking on an empty MVar read, only one will be woken up when someone writes to it.

STM has transactional semantics. You can write to multiple STM variables atomically in a transaction, and your transaction will restart if any of your input variables have been written to while your transaction was running. Useful if you need to maintain consistency between several variables at all times.

Async is a wrapper around STM and provides common functions you would often write yourself to express common patterns, but more battle tested.

Strategies are about optimistically evaluating Haskell thunks in parallel. For example if you map a function over a list, now you have a list of thunks. You can use Strategies to now evaluate those thunks in parallel. Key thing with Strategies is that if you remove them from your code (or use a non-threaded runtime) it doesn't affect the semantics of your program, it just might make it slower due to loss of parallel evaluation.

The Par Monad isn't used much, but the key idea there is you build an explicit dependency graph of the values in your computation and the runtime tries to parallelize the nodes in the graph that it can. Similar to what make does if you run it with -j.


Thanks, excellent summary!


I don’t think this an entirely fair comparison. STM provides essentially primitive operations, Async just builds a higher level API on top of them for common pattern. Not unlike the relationship between errgroup and go routines.

Strategies solve a different problem, in giving control of evaluation order of pure values. STM is aimed more at traditional multi threading


Also repa and accelerate :)

> I much prefer the Go approach, where there is a single canonical concurrency mechanism which is baked right into the language and is extremely well-documented.

Haskell doesn't have everything but I'd prefer something other than channels for concurrency. Actually, I like the Rust ecosystem in that regard (though it's missing something like accelerate...)




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: