We saw in an earlier section that an atom provides an atomic read-and-update operation. What if we need to perform an atomic read-and-update operation across two or even more atoms? This clearly poses a coordination problem. Some entity has to be watching over the process of reading and updating so that the values are not corrupted. This is what a ref provides—a system based on software transactional memory (STM). This takes care of concurrent atomic read-and-update operations across multiple refs such that either all updates go through or, in the case of failure, none do. Like atoms, on failure, refs retry the whole operation from scratch with new values.
Clojure's STM implementation is coarse grained—it works on the application level's objects and aggregates (references to aggregates), which are scoped to just all the refs in a program constituting Ref world. Any update to a ref can only happen synchronously in a transaction in a dosync
block of code...