io.github.frenchy64.fully-satisfies.head-releasing
Variants of clojure.core functions (and internal helpers)
that release the head of seqs earlier, enabling lower peak memory
usage in some cases.
For example, many higher-order functions in this namespace release strong
references to arguments before calling their function arguments.
Realizing the following example would be prone to failure with an OutOfMemoryError using
clojure.core/map because map retains a strong reference to takes-75-percent-of-heap
when calling its function argument.
(map (fn [takes-75-percent-of-heap]
(if (< (rand) 0.5)
takes-75-percent-of-heap
(generate-another-value-taking-75-percent-of-heap)))
[takes-75-percent-of-heap])
In contrast, using head-releasing/map, the garbage collector can reclaim takes-75-percent-of-heap
while calculating (generate-another-value-taking-75-percent-of-heap) because
head-releasing/map does not strongly reference takes-75-percent-of-heap at that point.
The basic implementation trick to achieving this is to call (rest s) on the seq currently
being processed _before_ calling (f (first f)), so the strong reference to (first f)
is transferred from the higher-order function to f during the call to f.
every?
added in 1.0
(every? pred coll)
Returns true if (pred x) is logical true for every x in coll, else
false.
head-releasing/every? is additionally:
- thread-safe for mutable collections
- does not hold strong reference to argument of pred when it is called.
keep
added in 1.2
(keep f)
(keep f coll)
Returns a lazy sequence of the non-nil results of (f item). Note,
this means false return values will be included. f must be free of
side-effects. Returns a transducer when no collection is provided.
Additionally, head-releasing/keep does not hold a strong reference
to the argument of f when it is called (unless a single chunked seq coll is provided).
keep-indexed
added in 1.2
(keep-indexed f)
(keep-indexed f coll)
Returns a lazy sequence of the non-nil results of (f index item). Note,
this means false return values will be included. f must be free of
side-effects. Returns a stateful transducer when no collection is
provided.
Additionally, head-releasing/keep-indexed does not hold a strong reference
to the argument of f when it is called (unless a single chunked seq coll is provided).
map
added in 1.0
(map f)
(map f coll)
(map f c1 c2)
(map f c1 c2 c3)
(map f c1 c2 c3 & colls)
Returns a lazy sequence consisting of the result of applying f to
the set of first items of each coll, followed by applying f to the
set of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments. Returns a transducer when
no collection is provided.
Additionally, head-releasing/map does not hold a strong reference
to the arguments of f when it is called (unless a single chunked seq coll is provided).
map-indexed
added in 1.2
(map-indexed f)
(map-indexed f coll)
Returns a lazy sequence consisting of the result of applying f to 0
and the first item of coll, followed by applying f to 1 and the second
item in coll, etc, until coll is exhausted. Thus function f should
accept 2 arguments, index and item. Returns a stateful transducer when
no collection is provided.
Additionally, head-releasing/map-indexed does not hold a strong reference
to the argument of f when it is called (unless a single chunked seq coll is provided).
mapcat
added in 1.0
(mapcat f)
(mapcat f & colls)
Returns the result of applying concat to the result of applying map
to f and colls. Thus function f should return a collection. Returns
a transducer when no collections are provided.
Additionally, head-releasing/mapcat does not hold strong references to
the arguments of f when it is called (unless a single chunked seq coll is provided).
naive-seq-reduce
(naive-seq-reduce s f val)
Reduces a seq, ignoring any opportunities to switch to a more
specialized implementation.
Additionally, head-releasing/naive-seq-reduce does not retain
a strong reference to the arguments of f when it is called.
not-any?
added in 1.0
(not-any? pred coll)
Returns false if (pred x) is logical true for any x in coll,
else true.
Additionally, head-releasing/not-any? does not hold a strong reference
to the argument of pred when it is called.
not-every?
added in 1.0
(not-every? pred coll)
Returns false if (pred x) is logical true for every x in
coll, else true.
Additionally, head-releasing/not-every? does not hold a strong reference
to the argument of pred when it is called.
some
added in 1.0
(some pred coll)
Returns the first logical true value of (pred x) for any x in coll,
else nil. One common idiom is to use a set as pred, for example
this will return :fred if :fred is in the sequence, otherwise nil:
(some #{:fred} coll)
Additionally, head-releasing/some does not hold a strong reference
to the argument of pred when it is called.