Use lazy sequences deliberately in Clojure, with clear boundaries around realization, retention, and side effects.
Lazy sequence: A sequence whose elements are produced on demand rather than all at once.
Lazy sequences are powerful because they let you describe a potentially large or even unbounded computation without realizing the whole result immediately. That makes pipelines expressive and can reduce wasted work.
But laziness is only a win when you understand the cost model. Otherwise it becomes a source of retained data, repeated work, confusing side effects, and performance surprises.
Lazy sequences are useful when:
1(->> (range)
2 (filter odd?)
3 (map #(* % %))
4 (take 5))
5;; => (1 9 25 49 81)
The sequence only computes what take demands.
Lazy code still becomes real somewhere. That point matters.
The questions to ask are:
1(def expensive-items
2 (map fetch-item ids))
That definition does not fetch everything immediately, but it also means fetching happens later at the moment of realization. If the consumer traverses the sequence twice, the effects and costs may surprise you unless the result is cached or materialized deliberately.
Side effects in lazy sequence steps are a common trap:
1(map #(do (println "processing" %)
2 (* % 2))
3 xs)
The println calls happen when values are realized, not when the sequence is constructed. That can make logs appear at odd times or multiple times.
If the work is fundamentally effectful, consider:
doall or into at a chosen boundaryLaziness can accidentally keep more data alive than you expect. Holding onto the head of a sequence while traversing deeper into it can retain already-seen elements. Chunked sequence behavior can also realize more than one element at a time.
That does not make lazy sequences bad. It means the abstraction is operationally real. You should know whether the data is:
When the workload is sensitive, use measurements, not assumptions.
Some workloads want a lazy pipeline. Others want an eager vector. Others want a transducer into a concrete target. Choose based on how the data will actually be consumed.
Good signs for laziness:
Bad signs:
When reviewing lazy-sequence code, ask:
Lazy sequences are valuable because they let you separate description from demand. They become risky when the demand boundary is accidental or invisible.