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 vWithdraw 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 totalExample
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?