Fork me on GitHub

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)))]
         ([] (rf))
         ([acc] (rf acc))
         ([acc x]
          (handle-input acc x)))))))
  (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 []
      (mapcat :x)
      (fn [acc x] (when x (reduced x)))
      (constantly nil))
    [{:x (range 10)}
     {:x (range 2)}])

  ;;[0 0]