liveBook · Manning

Ref

As explained in Clojure: The Essential Reference

A ref is a Clojure reference type whose coordinated state changes occur within software transactional memory transactions.

A ref holds shared state that must change in coordination with other refs. It is Clojure’s entry point to software transactional memory (STM): wrap related reads and changes in dosync so they belong to one transaction. Unlike atoms or agents, multiple refs can participate in that transaction without explicit locking. section 14.4 section 14.0

(def account-1 (ref 1000)) ; #1
(def account-2 (ref 500))

(defn transfer [amount]
  (dosync ; #2
    (when (pos? (- @account-1 amount))
      (alter account-1 - amount) ; #3
      (alter account-2 + amount))
    {:account-1 @account-1 :account-2 @account-2}))

(transfer 300) ; #4
;; {:account-1 700, :account-2 800}

The account-transfer example creates two refs, reads the first with @, and uses alter inside dosync to subtract 300 from one balance and add it to the other. The returned balances are 700 and 800. section 14.4

Update or replace a value

Given (def r (ref 0)), (dosync (alter r inc)) returns 1. A later (dosync (ref-set r 2)) returns 2. Use alter to compute a new value with a function; use ref-set to supply the replacement value. section 14.4

Other transaction operations serve different needs: commute is used for concurrent vote counting, while ensure appears where a transaction checks a separate ref before continuing. Transaction code can be retried: a slow reader with default history may restart after another transaction commits. Keep I/O out of it; io! raises an IllegalStateException when I/O is attempted in a transaction. section 14.4

(let [r (ref 0 :min-history 1)  ; #1
      T1 (future
           (dosync
             (println "T1 starts")
             (Thread/sleep 1000)
             (println "T1 history-count"
               (ref-history-count r))
             @r))
      T2 (future
           (dosync
             (println "T2 starts")
             (alter r inc)))] ; #2
  [@T1 @T2 @r])

;; T1 starts
;; T2 starts
;; T1 history count 1
;; [0 1 1] ; #3

Setting :min-history to 1 lets the slow reader in this example return the earlier value, 0, while the writer commits 1. Compare that result with the default-history example, where the reader restarts and returns 1. section 14.4

How Clojure: The Essential Reference covers it

How deep it goes
Works through transaction updates, concurrent retries, history settings, and an I/O failure without detailing STM implementation internals.
Examples
Uses an account transfer, concurrent vote counting, slow readers, and transaction I/O to demonstrate behavior.
What it assumes
Assumes familiarity with basic Clojure forms and dereferencing, while introducing STM through transaction examples.
What you can do after
Operate/configure it with ref, dosync, alter, ref-set, and :min-history to coordinate and inspect shared state.
How it connects
Links refs to software transactional memory, dosync, alter, ref-set, commute, ensure, futures, atoms, and agents.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.