Why does this work
(def fibs (cons 0 (cons 1 (lazy-seq (map + fibs (rest fibs))))))
(take 10 fibs)
while this other
(def fibs (lazy-seq (cons 0 (cons 1 (map + fibs (rest fibs))))))
(take 10 fibs)
generate a StackOverflowError?
Let's first note that it also works with one cons
outside:
(def fibs (cons 0 (lazy-seq (cons 1 (map + fibs (rest fibs))))))
Let's also establish that lazy-seq
is a macro (obviously, since it doesn't evaluate the arguments) and puts the body
(the argument(s) to lazy-seq
) into a function.
Now, if you "request" an element from that sequence that function will be called once and subsequent calls will return the cached value.
Obviously, only once that function returns something you have a value available. Now, what's happening in our bad case:
Imagine clojure is in the middle of evaluating the function for the very first time. The first thing it needs is +
, fibs
and (rest fibs)
in order to pass this to cons. Now clojure will see that fibs
is a lazy sequence and invoke it! This is because you're currently in your first invocation and haven't returned. This is causing an infinite recursion.
The fix is easy: Make sure there is a realized element in the beginning of the list so that the two expressions, fibs
and (rest fibs
) can return something. In particular, taking the rest
of (cons 1 whatever)
will return whatever
without realizing a single element (important since otherwise we'd have the same problem again).
lazy-seq
? It seems to run fine if you just remove it. – jmargolisvt