The classic toy example is the Bank that holds its account balances in the memory of a computer. It wants to make sure that when dollars are transferred from account to account, i.e. money is taken from an account and added to another, this happens as a complete transaction, or does not happen at all. Please read Simon Peyton Jones's paper on Haskell STM: Beautiful concurrency. I mean really, you should read it. Even though Simon stores his account balances in RAM :)
Anyways, you could solve the bank problem using Locks, but they are quite prone to deadlocks. In the STM model, there are no locks, but instead it "optimistically" tries to do your stuff and if a conflict with another thread occurs, it does a rollback and tries again. Nice? Here's how you could do it in Haskell:
As you see, I've even included balance checking: the withdraw function throws an exception if there's not enough money.
But hey! What if I do something irreversible in my transactional code? Now the system would not be able to rollback correctly!
That was my first (ok, second) thought when I read about how STM works. But Haskell solves this problem in the most elegant way: by using the STM Monad. When you're in this Monad, you cannot have irreversible side-effects. Just try adding a
withdrawfunction and you'll see it won't compile. As you see, in the above example, the former of the
doblocks is in the STM monad while the latter is in the IO monad.
Monads kick ass!
And no, I'm not writing an online banking system. But, I'd certainly use Haskell if I was.
Btw, when using STM in Clojure, is there really any guarantee that your transactional code doesn't have irreversible side-effects?