Thoughts on this:
A "higher order transducer" capture which takes a transducer, a reducing function and an init function of one argument.
It essentially wraps a transducer and captures all output from it, collecting it according to reducef and initf (applied to the first x it receives).
This guarantees that you will get one output for every input passed through the transducer. This is useful for working with tabular data, where you want to use a transducer to "append a column" (example comping soon).
I'm most uncertain about the arity of initf or if it should even be a function at all. It tended to be useful in practice but it seems inelegant in a way vs the traditional reduce signature.
I'm also wondering if there are any edge cases I've missed, especially surrounding reduced
(defn capture
([xf] (capture xf conj))
([xf reducef] (capture xf reducef (constantly (reducef))))
([xf reducef initf]
(fn [rf]
(let [a (volatile! [])
collect! (fn [_acc x]
(vswap! a reducef x))
init! (fn [x] (vreset! a (initf x)))
f (xf collect!)
handle-input (fn [acc x]
(init! x)
(f a x)
(rf acc (unreduced @a)))]
(fn
([] (rf))
([acc] (rf acc))
([acc x]
(handle-input acc x)))))))
(comment
(into []
(capture (partition-all 2))
(range 5))
;;=> [[] [[0 1]] [] [[2 3]] []]
(into []
(capture (dedupe)
(fn [acc x]
(reduced (if (some? x) x :na)))
(fn [x] :na))
[1 2 2 3 4 5 5 6])
;; [1 2 :na 3 4 5 :na 6]
(into []
(capture (mapcat :x))
[{:x (range 10)}
{:x (range 2)}])
;;[[0 1 2 3 4 5 6 7 8 9] [0 1]]
(into []
(capture
(mapcat :x)
(fn [acc x] (when x (reduced x)))
(constantly nil))
[{:x (range 10)}
{:x (range 2)}])
;;[0 0]
)