Is there a complete list of the lazy features of the Clojure central module?


After a while of working with Clojure, I have accumulated some knowledge on its laziness. I know whether a frequently-used API such as map is lazy. However, I still feel dubious when I start using an unfamiliar API such as with-open.

Is there any document that shows a complete list of lazy APIs of Clojure's core module?

You can find functions that return lazy sequences by opening up the Clojure code and searching for "Returns a lazy"

I am not aware of any curated lists of them.

The rule of thumb is: if it returns a sequence, it will be a lazy sequence, if it returns a value, it will force evaluation.

When using a new function, macro or special form, read the docstring. Most development environments have a key to show the docstring, or at least navigate to the source (where you can see the docstring), and there is always

In the case of with-open:

with-open macro Usage: (with-open bindings & body) bindings => [name init ...]

Evaluates body in a try expression with names bound to the values of the inits, and a finally clause that calls (.close name) on each name in reverse order.

We can see that the result of calling with-open is evaluation of the expression with a final close. So we know that there is nothing lazy about it. However that doesn't mean you don't need to think about laziness inside with-open, quite the opposite!

(with-open [r (io/reader "myfile")]
  (line-seq r))

This is a common trap. line-seq returns a lazy sequence! The problem here is that the lazy sequence will be realized after the file is closed, because the file is closed when exiting the scope of with-open. So you need to fully process the lazy sequence before exiting the with-open scope.

My advice is to avoid trying to think about your program as having 'lazy bits' and 'immediate bits', but instead just be mindful that when io or side-effects are involved you need to take care of when things happen as well as what should happen.