Supponi di avere alcuni conti bancari:
(def accounts
[(ref 0)
(ref 10)
(ref 20)
(ref 30)])
E una funzione "trasferimento" atomico:
(defn transfer [src-account dest-account amount]
(dosync
(alter dest-account + amount)
(alter src-account - amount)))
Che funziona come segue:
(transfer (accounts 1) (accounts 0) 5)
(map deref accounts)
=> (5 5 20 30)
È quindi possibile comporre facilmente la funzione di trasferimento per creare una transazione di livello superiore, ad esempio il trasferimento da più account:
(defn transfer-from-all [src-accounts dest-account amount]
(dosync
(doseq [src src-accounts]
(transfer src dest-account amount))))
(transfer-from-all
[(accounts 0) (accounts 1) (accounts 2)]
(accounts 3)
5)
(map deref accounts)
=> (0 0 15 45)
Si noti che tutti i trasferimenti multipli avvenivano in un'unica transazione combinata, cioè era possibile "comporre" le transazioni minori.
Per fare ciò con i blocchi si complicherebbe molto rapidamente: supponendo che gli account dovessero essere bloccati individualmente, bisognerebbe fare qualcosa come stabilire un protocollo sull'ordine di acquisizione dei blocchi per evitare deadlock. È molto facile fare un errore difficile da rilevare. STM ti salva da tutto questo dolore.