Refs are for coordinated, synchronous state changes across multiple values. Clojure’s STM (Software Transactional Memory) lets you update multiple refs as one atomic transaction—without explicit locks.
Use refs when:
- you must update multiple pieces of state together (invariants across refs)
- you want atomicity and consistency across those updates
Create and Read
1(def checking (ref 100))
2(def savings (ref 200))
3
4@checking ; => 100
Update with dosync + alter
All ref updates must happen inside a transaction:
1(dosync
2 (alter checking - 10)
3 (alter savings + 10))
If the transaction conflicts with another transaction, it may be retried automatically.
ref-set and commute
ref-set replaces the ref’s value (still inside dosync).
commute is an optimization for commutative updates (like adding to a counter) where the STM can reorder safely.
Important: Avoid Side Effects Inside dosync
Because transactions can retry, doing I/O inside dosync can repeat side effects.
The common pattern is:
- compute the new values transactionally
- perform side effects (logging, I/O) outside the transaction using the committed result
Knowledge Check: Refs / STM
### What is the main purpose of refs?
- [x] Coordinated, atomic updates across multiple pieces of shared state.
- [ ] Fast indexed access to data.
- [ ] Asynchronous background work.
- [ ] Java class loading.
> **Explanation:** Use refs when you need to update multiple related values together and preserve invariants.
### Why should you avoid side effects inside `dosync`?
- [x] Because the transaction may retry, which could repeat the side effect.
- [ ] Because refs don’t allow calling functions.
- [ ] Because `dosync` runs only at compile time.
- [ ] Because STM disables exceptions.
> **Explanation:** Retries are a feature of STM conflict resolution. Side effects inside a retried transaction can run multiple times.
### Which function is commonly used to update a ref inside a transaction?
- [x] `alter`
- [ ] `swap!`
- [ ] `reset!`
- [ ] `send`
> **Explanation:** `alter` changes a ref’s value within a `dosync` transaction. `swap!`/`reset!` are atom operations; `send` is for agents.