-- | 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)