mapcat* is a book-specific alternative to mapcat for cases where the usual mapcat expression evaluates its mapped input too eagerly. It retains mapcat-style flattening while delaying concatenation through a lazy step. section 10.4
The implementation follows a small, composable pipeline:
- Accept a transformation function and zero or more collections. section 10.4
- Apply map to produce the transformed collections. section 10.4
- Walk those results lazily, concatenating the first result before recurring on the remainder. section 10.4
Deferring mapped-input evaluation
A regular mapcat expression can print dots while the value is defined: (def a (mapcat range (map #(do (print ".") %) (into () (range 10))))) prints "....". Replacing mapcat with mapcat* uses the lazy step shown in its definition, avoiding that initial eager evaluation. section 10.4
(defn mapcat* [f & colls]
(letfn [(step [colls]
(lazy-seq
(when-first [c colls]
(concat c (step (rest colls))))))]
(step (apply map f colls)))) ; #1
(def a (mapcat* range (map #(do (print ".") %) (into () (range 10))))) ; #2
Inspect the complete definition to see how mapcat* combines map, lazy-seq, when-first, and concat. section 10.4
Use mapcat* when preserving mapcat’s transformation-and-concatenation behavior matters, but the first evaluation of mapped arguments should remain deferred. section 10.4
