Transient operations
spork-lang 0.6.0
Mutable builders for efficient batches over persistent collections.
Transients are mutable builders initialized from persistent collections. Operations ending in ! mutate a transient in place; do not apply them to persistent collections. Convert the builder back with persistent! when the batch is complete.
transient#
Creates a mutable builder from a persistent Vector, DoubleVector, IntVector, Map, Set, or SortedVector.
(def tv (transient [1 2 3]))
(def tm (transient {:a 1}))
(def ts (transient #{1 2}))
persistent!#
Converts a transient back to a persistent collection and invalidates the transient. Later access to the transient raises RuntimeError.
(persistent! (transient [1 2 3])) ; => [1 2 3]
; Common pattern: build then persist
(-> (transient [])
(conj! 1)
(conj! 2)
(conj! 3)
(persistent!)) ; => [1 2 3]
conj!#
Mutates a transient by adding one value and returns that transient. A transient map requires a two-item key/value pair.
(def tv (transient []))
(conj! tv 1)
(conj! tv 2)
(persistent! tv) ; => [1 2]
assoc!#
Associates a key in a TransientMap or an index in a general TransientVector and returns the transient. Vector indices may be negative. Typed-vector and sorted-vector transients do not support this operation.
(def tm (transient {:a 1}))
(assoc! tm :b 2)
(assoc! tm :c 3)
(persistent! tm) ; => {:a 1 :b 2 :c 3}
(def tv (transient [1 2 3]))
(assoc! tv 1 42)
(persistent! tv) ; => [1 42 3]
dissoc!#
Removes from a transient map.
(def tm (transient {:a 1 :b 2 :c 3}))
(dissoc! tm :b)
(persistent! tm) ; => {:a 1 :c 3}
disj!#
Removes a value from a transient set or sorted vector.
(def ts (transient #{1 2 3 4}))
(disj! ts 2)
(disj! ts 4)
(persistent! ts) ; => #{1 3}
pop!#
Removes the final element from a general TransientVector. Typed-vector and sorted-vector transients do not support this operation.
(def tv (transient [1 2 3 4]))
(pop! tv)
(pop! tv)
(persistent! tv) ; => [1 2]
SortedVector Transient Operations#
SortedVector has its own transient type with methods that maintain sorted order:
; Create transient from sorted vector
(def sv (sorted-vec [1 3 5 7]))
(def tsv (transient sv))
; Add elements while maintaining sorted order
(conj! tsv 2)
(conj! tsv 4)
(conj! tsv 6)
; Remove one matching value; an absent value is a no-op
(disj! tsv 3)
(disj! tsv 99)
; Convert back to persistent
(def result (persistent! tsv)) ; => sorted_vec(1, 2, 4, 5, 6, 7)
; A transient preserves its source's key function and reverse ordering
(def tsv
(transient
(sorted-vec [{:score 10} {:score 20}]
*{:key :score :reverse true})))
(conj! tsv {:score 15})
(vec (persistent! tsv))
; => [{:score 20} {:score 15} {:score 10}]
with-mutable#
Binds a transient initialized from the supplied collection, executes the body, and returns that transient's persistent result. The body's own result is ignored. This macro is the shortest form for a scoped batch of mutations.
(with-mutable [v [10 20]]
(conj! v 30)
:ignored-body-result)
; => [10 20 30]
Python-style Mutable APIs:
Transient maps, vectors, and sets are registered with Python's mutable collection ABCs:
TransientMappassesisinstancechecks forMutableMappingTransientVectorpassesisinstancechecks forMutableSequenceTransientSetpassesisinstancechecks forMutableSet
ABC registration enables type checks but does not supply every Python mixin method: for example, transient vectors have no .insert, and transient maps have no .update. They can be passed to Python code that relies only on supported operations. Typed-vector and sorted-vector transients have smaller, type-specific APIs.
; TransientVector supports .append, .extend, indexing, and iteration
(with-mutable [v []]
(v.extend [1 2 3])
(v.append 4))
; => [1 2 3 4]
; TransientMap supports .get, .keys, .values, .items, and iteration
(with-mutable [m {}]
(assoc! m :a 1)
(assert (= (m.get :a) 1)))
; => {:a 1}
; TransientSet supports .add, .discard, .remove, .clear, and iteration
(with-mutable [s #{}]
(s.add 1)
(s.add 2)
(s.discard 1))
; => #{2}
For example, Python's random.shuffle mutates a transient vector through the mutable-sequence protocol, and with-mutable retains the mutation in its persistent result:
(ns example.shuffle
(:import [random :refer [shuffle]]))
(def shuffled
(with-mutable [v [1 2 3 4]]
(shuffle v)))
; The order is random, but the persistent result contains the same values
(vec (sorted shuffled)) ; => [1 2 3 4]
Typical Transient Pattern:
(defn build-vector [n]
(loop [tv (transient [])
i 0]
(if (< i n)
(recur (conj! tv i) (inc i))
(persistent! tv))))
(build-vector 5) ; => [0 1 2 3 4]