Integer -> Account -> IO () debit amount account = do current <- takeMVar account putMVar account (current - amount) Using such procedures will ensure that money will never be lost or gained due to improper interleaving of reads and writes to any individual account. However, if one tries to compose them together to create a procedure like transfer: transfer :: Integer -> Account -> Account -> IO () transfer amount from to = do debit amount from credit amount to a race condition still exists: the first account may be debited, then execution of the thread may be suspended, leaving the accounts as a whole in an inconsistent state. Thus, additional locks must be added to ensure correctness of composite operations, and in the worst case, one might need to simply lock all accounts regardless of how many are used in a given operation. Atomic transactions To avoid this, one can use the STM monad, which allows one to write atomic transactions. This means that all operations inside the transaction fully complete, without any other threads modifying the variables that our transaction is using, or it fails, and the state is rolled back to where it was before the transaction was begun. In short, atomic transactions either complete fully, or it is as if they were never run at all. The lock-based code above translates in a relatively straightforward way: type Account = TVar Integer credit :: Integer -> Account -> STM () credit amount account = do current <- readTVar account writeTVar account (current + amount) debit :: Integer -> Account -> STM () debit amount account = do current <- readTVar account writeTVar account (current - amount) transfer :: Integer -> Account -> Account -> STM () transfer amount from to = do debit amount from credit amount to The return types of STM () may be taken to indicate that we are composing scripts for transactions. When the time comes to actually execute such a transaction, a function atomically :: STM a -> IO a is used. The above implementation will make sure that no other transactions interfere with the variables it is using (from and to) while it is executing, allowing the developer to be sure that race conditions like that above are not encountered. More improvements can be made to make sure that some other "business logic" is maintained in the system, i.e. that