I wrote the following:
(fn r [f xs] (lazy-seq (if (empty? xs) '() (cons (f (first xs)) (rf (rest xs))))))
solve the problem 4clojure.com # 118: http://www.4clojure.com/problem/118
which asks to redefine the map without using the map, etc., and this solution passes the tests (I don’t know if it is right or not: it is very close to other solutions that say).
Since the problem was that it should have been lazy, I wrote the code above, “wrapping” my solution in lazy seq ... However, I do not understand how lazy-seq works.
I don’t understand what is “lazy” here, and how I could check it.
When I ask (type ...)
, I get, not surprisingly, clojure.lang.LazySeq, but I don’t know what is the difference between this and what I get if I just remove lazy-seq "wrapping".
Now, of course, if I remove lazy-seq, I get stackoverflow trying to execute this:
(= [(int 1e6) (int (inc 1e6))] (->> (... inc (range)) (drop (dec 1e6)) (take 2)))
Otherwise (i.e.: if I allow lazy-seq-packaging in place), it works fine.
Therefore, I decided to try to somehow "debug" / track what is happening in order to try to understand how it all works. I took the following macro (which I found on SO IIRC):
(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#))
And wrapped up the working version inside the dbg macro and tried to execute it again. And now kaboom: the version that worked fine now also calls stackoverflow.
Now I'm not sure: maybe this is an undesirable effect of a macro that somehow forces us to analyze things that would not be evaluated otherwise?
It would be great if someone could explain, using this simple function and simple test, how laziness works here, what exactly is called when, etc.