🎓 A1Learner
🔍
0 of 47 lessons completed

Haskell STM Composition

STM composition joins small transactions into one larger transaction. Small STM actions combine inside a single atomically block.

Add one and then double in one transaction

Start with 10. Combine an add-one action and a double action inside one transaction.

import Control.Concurrent.STM

incT :: TVar Int -> STM ()
incT t = modifyTVar' t (+ 1)

doubleT :: TVar Int -> STM ()
doubleT t = modifyTVar' t (* 2)

main :: IO ()
main = do
  t <- newTVarIO (10 :: Int)
  atomically $ do
    incT t
    doubleT t
  v <- readTVarIO t
  print v

Withdraw and deposit combine into one transfer

Start with 100 and 20. Combine a withdraw action and a deposit action inside one transaction.

import Control.Concurrent.STM

withdrawT :: TVar Int -> Int -> STM ()
withdrawT t amount = modifyTVar' t (subtract amount)

depositT :: TVar Int -> Int -> STM ()
depositT t amount = modifyTVar' t (+ amount)

main :: IO ()
main = do
  a <- newTVarIO (100 :: Int)
  b <- newTVarIO (20 :: Int)
  atomically $ do
    withdrawT a 30
    depositT b 30
  x <- readTVarIO a
  y <- readTVarIO b
  print (x, y)

Two reads combine into one total

Start with 19 and 23. Combine two read actions into one total inside one transaction.

import Control.Concurrent.STM

sumT :: TVar Int -> TVar Int -> STM Int
sumT a b = do
  x <- readTVar a
  y <- readTVar b
  return (x + y)

main :: IO ()
main = do
  a <- newTVarIO (19 :: Int)
  b <- newTVarIO (23 :: Int)
  total <- atomically (sumT a b)
  print total

Example

import Control.Concurrent.STM

takeStock :: TVar Int -> Int -> STM ()
takeStock t n = modifyTVar' t (subtract n)

addSold :: TVar Int -> Int -> STM ()
addSold t n = modifyTVar' t (+ n)

addRevenue :: TVar Int -> Int -> STM ()
addRevenue t amount = modifyTVar' t (+ amount)

main :: IO ()
main = do
  stock <- newTVarIO (100 :: Int)
  sold <- newTVarIO (0 :: Int)
  revenue <- newTVarIO (0 :: Int)
  atomically $ do
    takeStock stock 4
    addSold sold 4
    addRevenue revenue 40
  x <- readTVarIO stock
  y <- readTVarIO sold
  z <- readTVarIO revenue
  print (x, y, z)

Key points

  • Composition: composition joins small STM actions into one larger transaction.
  • Small action: a small STM action reads or changes one box.
  • Single atomically: one atomically block runs the combined actions as one transaction.
  • Separate blocks: two atomically blocks are two separate transactions, not one combined transaction.
  • Order: combined actions run in the order they appear in the do block.

In this example, three small STM actions describe one sale. The first takes 4 from stock. The second adds 4 to sold. The third adds 40 to revenue. One atomically block runs all three together, and the main thread prints 96, 4, and 40.

Note

a helper that returns an STM action can be reused inside many transactions.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Haskell Playground

Exercise

How do two small STM actions become one transaction?