chapter twenty

20 Mutation and Side Effects

 

Mutation.

20.1  transient, persistent!, conj!, pop!, assoc!, dissoc! and disj!

A transient is a mutable state available to some of the Clojure persistent data structures. Currently supported collection types are: vectors, array-maps, hash-maps and sets.

When a collection enters transient state, it stops supporting typical persistent functions like assoc or conj preventing the accidental sharing of the transient:

(def v (transient []))

(conj v 1)
;; ClassCastException
;; clojure.lang.PersistentVector$TransientVector
;; cannot be cast to clojure.lang.IPersistentCollection

While a subset of read-only functions like get, nth or count still works, an entire new set of functions is available to mutate a transient. Their name is like other standard functions with the conventional "!" added at the end:

(def v (transient []))
(def s (transient #{}))
(def m (transient {}))

((conj! v 0) 0) ;      #1
;; 0

((conj! s 0) 0) ;      #2
;; 0

((assoc! m :a 0) :a) ; #3
;; 0

The main use case for transient is to enable controlled and isolated mutation to and from persistent data structures, removing the overhead associated with creating many persistent copies that are never going to be shared. The standard library itself has plenty of such examples: into, mapv, group-by, set, frequencies (and more), are functions transforming a collection into another using transient to speed up internal processing.

20.2  doseq, dorun, run!, doall, do

20.3  volatile!, vreset!, vswap! and volatile?

20.4  set!