Why does with-meta realize lazy sequences?

$ clj
Clojure 1.10.1
(-> (map #(do (println %)
              (* 10 %))
         (range 9))
    (with-meta {:some-metadata "some-metadata"})
    meta)
0
1
2
3
4
5
6
7
8
{:some-metadata "some-metadata"}
2 Likes

It’s a common Clojure gotcha if you don’t know about chunked sequences. (range 10) and (vector 1 2 3 4 5) are chunked sequences, but (range) and (list 1 2 3 4 5) are not. You can check it with chunked-seq? predicate.

Chunking means to be processed by 32-length chunks. Thus when you do (first (map str (range))) first 32 items of the lazy-seq would be realized.

When you invoke with-meta on a lazy-seq, seq will be invoked on the original sequence. Therefore either the first element or 32 first elements will be realized depending on type of the sequence.

Back to the original example, if you pass in a list instead of LongRange, you will see only the first element printed.

3 Likes

+1 for @zelark !
You can double check the CHUNK_SIZE in clojure.lang.Range.java.
Furthermore, just try this and you’ll see that it stops at 31

(-> (map #(do (println %)
                    (* 10 %))
               (range 33))
          (with-meta {:some-metadata "some-metadata"})
          meta)
1 Like

Thank you so much @zelark @BaptisteDupuch!
This makes sense now : ).