# Concurrency: Threads, MVar, STM and async — Haskell

Source: https://www.geekswithgeeks.com/en/haskell/r-concurrency

> Write concurrent programs with lightweight threads and software transactional memory.

## Concurrency without fear

GHC's runtime provides **lightweight green threads**: `forkIO` starts one cheaply, and thousands are normal. Compile with `-threaded` and run with `+RTS -N` to use all CPU cores. Communication options: **`MVar`**, a box that is either empty or full, usable as a lock or a one-slot channel; **`Chan`** for unbounded queues; and **Software Transactional Memory (STM)**, Haskell's standout feature. With STM, you read and write **`TVar`**s inside an `STM` transaction and run it with **`atomically`**: transactions are composable, all-or-nothing and free of deadlocks from lock ordering. **`retry`** blocks a transaction until the variables it read change (perfect for "wait until there is enough stock"), and `orElse` composes alternatives. Purity makes STM practical: the type system guarantees that a transaction performs no irreversible I/O, so it can be safely re-run. The **`async`** library provides high-level tools: `concurrently`, `race`, `mapConcurrently` and `withAsync`, with proper exception propagation and cancellation. For parallel pure computation, the `parallel` package offers strategies such as `parMap`.

## Bank transfers with STM and concurrent fetches with async

atomically, retry and mapConcurrently.

```haskell
import Control.Concurrent.Async (mapConcurrently, concurrently)
import Control.Concurrent.STM
import Control.Concurrent (threadDelay)
import Control.Monad (when)

transfer :: TVar Int -> TVar Int -> Int -> STM ()
transfer from to amount = do
  balance <- readTVar from
  when (balance < amount) retry           -- block until 'from' has enough money
  modifyTVar' from (subtract amount)
  modifyTVar' to (+ amount)

fetchPrice :: String -> IO (String, Int)
fetchPrice sku = do
  threadDelay 100000                      -- simulate a 100 ms network call
  pure (sku, 4950)

main :: IO ()
main = do
  asha <- newTVarIO 1000
  ravi <- newTVarIO 0
  _ <- concurrently
         (atomically (transfer ravi asha 500))   -- waits until ravi has 500
         (atomically (transfer asha ravi 700))   -- runs first, then unblocks the other
  balances <- atomically ((,) <$> readTVar asha <*> readTVar ravi)
  print balances                                 -- (800,200)

  prices <- mapConcurrently fetchPrice ["pen", "ink", "pad"]   -- all three in parallel
  print prices
```

## Keep IO out of transactions

STM transactions may be retried many times, so they must not perform I/O. The type system enforces this: you cannot call `putStrLn` inside `STM`, only in IO before or after `atomically`.

**Quiz:** What does retry do inside an STM transaction?

- [x] Aborts the transaction and blocks until one of the TVars it read changes
- [ ] Retries immediately in a busy loop
- [ ] Throws an exception
- [ ] Commits partially

*Answer:* Aborts the transaction and blocks until one of the TVars it read changes. retry waits for relevant state to change before re-running the transaction.
