ACME-0.0.0.0: Acme/Money.hs
-- | 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)