-- | A monad to handle economic transactions between computations module Acme.Money (Money, lift', charge, purchase, runMoneys) where import Control.Monad.Reader import Control.Monad.Trans import Data.IORef newtype Money u t = Money { unMoney :: ReaderT (Int{-current-}, Int{-purchaser-}, [(IORef Float{-balance-}, Money u u{-computation-})]) IO t } instance Monad (Money u) where return = Money . return Money m >>= f = Money $ m >>= unMoney . f -- | Lift an IO computation into the Money monad lift' = Money . lift -- | Charge n simoleons from the purchasing computation charge n = if n < 0 then error "Charity begins at home" else Money (ask >>= \(i, j, ls) -> let ref = fst (ls !! j) in lift $ readIORef ref >>= \m -> if m >= n then writeIORef ref (m - n) >> modifyIORef (fst (ls !! i)) (+n) else error "Wallet lint") -- | Purchase the ith computation from the list purchase i = Money (ask >>= \(j, _, ls) -> lift $ runReaderT (unMoney $ snd (ls !! i)) (i, j, ls)) -- | Run several computations, where the computations may purchase the use of one another runMoneys ls = map (\(i, (_, m)) -> runReaderT (unMoney m) (i, -1, ls)) (zip [0..] ls)