Variants of clojure.core functions that improve thread-safety and general robustness when passed mutating collections.
We agree that 'Robust programs should not mutate arrays or Iterables that have seqs on them.' https://clojure.org/reference/sequences Eductions inhabit a middle ground and might be the most practical application of this namespace. They are designed to be walked from first to last like seqs, but each element is recomputed instead of being cached like persistent seqs. This becomes problematic if a sequence function walks its argument multiple times without first binding a seq.
For example, clojure.core/split-at could disagree on the take/drop parts of the collection if the coll is mutated between realizing the splits. Here, any one call to the eduction alternates between [0 1 2 3 4 5 6 7 8 9] and [9 8 7 6 5 4 3 2 1 0]. However, split-at incorrectly splits the eduction as [[0 1 2 3 4] [4 3 2 1 0]], and safer/split-at returns one of the two correct splits: [[0 1 2 3 4] [5 6 7 8 9]].
(deftest split-at-mutation-test (let [up-down (atom true) ed (eduction (map (fn [i] (when (zero? i) (swap! up-down not)) (if @up-down (- 9 i) i))) (range 10))] (is (= [[0 1 2 3 4] [4 3 2 1 0]] (split-at 5 ed))) (is (= [[0 1 2 3 4] [5 6 7 8 9]] (safer/split-at 5 ed)))))
See io.github.frenchy64.fully-satisfies.safer-test for more details.
The basic trick here is strategically calling seq earlier on the collection argument.
Variants of clojure.core functions that improve thread-safety and general robustness when passed mutating collections. We agree that 'Robust programs should not mutate arrays or Iterables that have seqs on them.' https://clojure.org/reference/sequences Eductions inhabit a middle ground and might be the most practical application of this namespace. They are designed to be walked from first to last like seqs, but each element is recomputed instead of being cached like persistent seqs. This becomes problematic if a sequence function walks its argument multiple times without first binding a seq. For example, clojure.core/split-at could disagree on the take/drop parts of the collection if the coll is mutated between realizing the splits. Here, any one call to the eduction alternates between [0 1 2 3 4 5 6 7 8 9] and [9 8 7 6 5 4 3 2 1 0]. However, split-at incorrectly splits the eduction as [[0 1 2 3 4] [4 3 2 1 0]], and safer/split-at returns one of the two correct splits: [[0 1 2 3 4] [5 6 7 8 9]]. (deftest split-at-mutation-test (let [up-down (atom true) ed (eduction (map (fn [i] (when (zero? i) (swap! up-down not)) (if @up-down (- 9 i) i))) (range 10))] (is (= [[0 1 2 3 4] [4 3 2 1 0]] (split-at 5 ed))) (is (= [[0 1 2 3 4] [5 6 7 8 9]] (safer/split-at 5 ed))))) See io.github.frenchy64.fully-satisfies.safer-test for more details. The basic trick here is strategically calling seq earlier on the collection argument.
(butlast coll)
Return a seq of all but the last item in coll, in linear time
safer/butlast additionally:
Return a seq of all but the last item in coll, in linear time safer/butlast additionally: - is threadsafe for mutable collections, at the cost of an additional call to seq. - calls next once per element instead of twice.
(drop-last coll)
(drop-last n coll)
Return a lazy sequence of all but the last n (default 1) items in coll
safer/drop-last additionally is thread-safe for mutable collections, at the cost of a call to seq.
Return a lazy sequence of all but the last n (default 1) items in coll safer/drop-last additionally is thread-safe for mutable collections, at the cost of a call to seq.
(every? pred coll)
Returns true if (pred x) is logical true for every x in coll, else false.
safer/every? additionally is thread-safe for mutable collections.
Returns true if (pred x) is logical true for every x in coll, else false. safer/every? additionally is thread-safe for mutable collections.
(last coll)
Return the last item in coll, in linear time
safer/last additionally:
Return the last item in coll, in linear time safer/last additionally: - is thread-safe for mutable collections, at the cost of a call to seq. - calls next once every step instead of twice.
(not-every? pred coll)
Returns false if (pred x) is logical true for every x in coll, else true.
safer/not-every? additionally is thread-safe for mutable collections.
Returns false if (pred x) is logical true for every x in coll, else true. safer/not-every? additionally is thread-safe for mutable collections.
(partitionv-all n)
(partitionv-all n coll)
(partitionv-all n step coll)
Returns a lazy sequence of vector partitions, but may include partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided.
safer/partitionv-all additionally is thread-safe for mutable collections and generally robust against a mutating coll.
Returns a lazy sequence of vector partitions, but may include partitions with fewer than n items at the end. Returns a stateful transducer when no collection is provided. safer/partitionv-all additionally is thread-safe for mutable collections and generally robust against a mutating coll.
(sort coll)
(sort comp coll)
Returns a sorted sequence of the items in coll. If no comparator is supplied, uses compare. comparator must implement java.util.Comparator. Guaranteed to be stable: equal elements will not be reordered. If coll is a Java array, it will be modified. To avoid this, sort a copy of the array.
safer/sort additionally is thread-safe for mutable collections (avoids NPE).
Returns a sorted sequence of the items in coll. If no comparator is supplied, uses compare. comparator must implement java.util.Comparator. Guaranteed to be stable: equal elements will not be reordered. If coll is a Java array, it will be modified. To avoid this, sort a copy of the array. safer/sort additionally is thread-safe for mutable collections (avoids NPE).
(sort-by keyfn coll)
(sort-by keyfn comp coll)
Returns a sorted sequence of the items in coll, where the sort order is determined by comparing (keyfn item). If no comparator is supplied, uses compare. comparator must implement java.util.Comparator. Guaranteed to be stable: equal elements will not be reordered. If coll is a Java array, it will be modified. To avoid this, sort a copy of the array.
safer/sort-by additionally is thread-safe for mutable collections (avoids NPE).
Returns a sorted sequence of the items in coll, where the sort order is determined by comparing (keyfn item). If no comparator is supplied, uses compare. comparator must implement java.util.Comparator. Guaranteed to be stable: equal elements will not be reordered. If coll is a Java array, it will be modified. To avoid this, sort a copy of the array. safer/sort-by additionally is thread-safe for mutable collections (avoids NPE).
(split-at n coll)
Returns a vector of [(take n coll) (drop n coll)]
safer/split-at additionally is thread-safe for mutable collections and generally robust against a mutating coll at the cost of a call to seq.
Returns a vector of [(take n coll) (drop n coll)] safer/split-at additionally is thread-safe for mutable collections and generally robust against a mutating coll at the cost of a call to seq.
(split-with pred coll)
Returns a vector of [(take-while pred coll) (drop-while pred coll)]
safer/split-with additionally is thread-safe for mutable collections, and generally robust against a mutating coll at the cost of a call to seq.
Returns a vector of [(take-while pred coll) (drop-while pred coll)] safer/split-with additionally is thread-safe for mutable collections, and generally robust against a mutating coll at the cost of a call to seq.
(splitv-at n coll)
Returns a vector of [(into [] (take n) coll) (drop n coll)]
safer/splitv-at additionally is thread-safe for mutable collections and generally robust against a mutating coll.
Returns a vector of [(into [] (take n) coll) (drop n coll)] safer/splitv-at additionally is thread-safe for mutable collections and generally robust against a mutating coll.
(take-last n coll)
Returns a seq of the last n items in coll. Depending on the type of coll may be no better than linear time. For vectors, see also subvec.
safer/drop-last additionally is thread-safe for mutable collections.
Returns a seq of the last n items in coll. Depending on the type of coll may be no better than linear time. For vectors, see also subvec. safer/drop-last additionally is thread-safe for mutable collections.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close