Search code examples
clojurefunctional-programminglazy-evaluationchunking

laziness does not work as expected


(defn seq-trial
  []
  (map #(do (println "hello " %) (inc %)) (range 10)))

(take 3 (seq-trial))

The code snippt above when evaluated prints out the following -

(hello 0 hello 1 hello 2 hello 3 hello 4 hello 5 hello 6 hello 7 hello 8 hello 9 1 2 3)

Because map returns a lazy sequence I expected this to print only -

(hello 0 hello 1 hello 2 1 2 3)

Why is the entire list evaluated here?


Solution

  • This is because of a performance optimization called chunking. Essentially the sequence is realised in groups of n items called chunks. This means you need to take care with any side effects in your mapping function. The end result is correct tho, you still get a sequence of the correct length returned

    The default chunk size is 32, so if you increase your range to a value larger than that you will understand what's going on a bit better:

    user> (defn seq-trial
      []
      (map #(do (println "hello " %) (inc %)) (range 100)))
    
    user> (take 3 (seq-trial))
    hello  0 ; 32 item 'chunk' realized...
    hello  1
    
    ...
    
    hello  30
    hello  31
    (1 2 3)  ; the expected returned value
    

    If you need to avoid chunking there are options available