Cracking my head with Clojure laziness
- clojure
Recently I was working in personal Clojure project. I'm aware of the laziness behaviour of Clojure, and I love it. Even knowing it, some issues you will only realize when you first met it.
Giving some context, my initial version was up and running, and I was very happy. After some days, I decided to do some improvements on the code.
(defn do-something-strict []
(->> (extract-contents)
(map content->data)
(remove already-exist?)
(map persist-data)
(do-final-thing)))
Everything was fine. So I started to some refactor. But the most innocent of the changes made my code stop working.
(defn do-final-thing [c]
(debug (str "Collection data: " c))
...)
The do-final-thing
did some debugging log, and I remove this line. Can you imagine what it caused?
If you do the following snippet in REPL, you got:
(remove odd? (range 1 10))
; => (2 4 6 8)
But if you do:
(defn foo []
(map prn (range 1 10))
"bar")
(foo)
; => "bar"
You will not see the prints on REPL. This is due to the laziness of map
. The same applies to
remove
and some other collection functions.
When you run on the REPL it has to print the result of the function call, this is why remove
example worked.
This detail made me lost quite some time investigating it. How did I fix it?
(defn do-something-strict []
(let [contents (extract-contents)
data (map content->data contents)
newer (remove already-exist? data)]
(doall
(map persist-data))))
Since I want the function eval all data, I had to use the doall
. My initial code only worked
because the debug
did eval the lazy collection.