I'll try and explain. This explanation isn't perfectly rigorous, but may be close enough.
(def fibs (lazy-seq (cons 0 (cons 1 (map +' fibs (rest fibs)))))) This creates a thunk, deferring execution of the body of `lazy-seq`. (first fibs) This forces the thunk, let's call it THUNK1. In order to force the thunk, we must evaluate the expression (cons 0 (cons 1 (map +' fibs (rest fibs)))) In order to evaluate the expression (cons 0 E1), we must evaluate E1 (cons 1 (map +' fibs (rest fibs))) In order to evaluate the expression (cons 1 E2), we must evaluate E2 (map +' fibs (rest fibs)) Although map creates a lazy seq, it does not lazily evaluate its argument expressions. fibs is fine (rest fibs) must be evaluated To evaluate (rest fibs), we have to go through basically the same process as when we started evaluating (first fibs) If we could just say "(rest fibs) is a thunk", then we'd be done. But that's not how rest works. It evaluates its argument. Evaluating fibs starts forcing THUNK1 again. Loop ad infinitum. So I think the answer is that "map" and "rest" are just a tiny bit less lazy than you might have thought. More laziness could be accomplished by making map a macro that doesn't eval its argument expressions, and by making rest always return a lazy seq. Either of these things would have prevented the infinite loop here. I won't bother getting into the discussion on why they are the way they are. Contrast with (def fibs (cons 0 (cons 1 (lazy-seq (map +' fibs (rest fibs)))))) This creates a seq, where the first two elements are already evaluated to be 0 and 1, but the rest after that is a thunk which is evaluated lazily. (first fibs) This is able to retrieve 0 immediately. It doesn't even force the lazy-seq thunk. How about something more interesting? (third fibs) We skip over 0 and 1, and now we have to start evaluating that lazy-seq. (map +' fibs (rest fibs)) Let's call this THUNK0 map of course produces a lazy seq, but we are forcing it to tell us its first result In order to do so, map only needs to access the first element of each of its args. (first fibs) immediately retrieve 0 (first (rest fibs)) We must evaluate (rest fibs) We immediately know this is (cons 1 THUNK0) (first (cons 1 THUNK0)) We immediately know this is 1, no need to recurse into forcing THUNK0 (+ 0 1) = 1 And there's our answer. When you go asking for the fourth element, it will now be able to access the third and second immediately with no recursive thunk forcing. Exercise: You put the lazy seq at the beginning and it hit an infinite loop. You put the lazy seq after the first two hard-coded values, and it worked. What happens when you put it after just the first hard-coded value? Can you explain why? (def fibs (cons 0 (lazy-seq (cons 1 (map +' fibs (rest fibs)))))) -- Dan Burton On Mon, Sep 4, 2017 at 5:33 PM, mrwizard82d1 <mrwizard8...@gmail.com> wrote: > I'm trying to understand infinite sequences. > > Wikipedia defines an implementation of the Fibonacci sequence as: > > (def fibs (cons 0 (cons 1 (lazy-seq (map +' fibs (rest fibs)))))) > > However, when I wrote fibs in the repl as: > > (def fibs (lazy-seq (cons 0 (cons 1 (map +' fibs (rest fibs)))))) > > executing (first fibs) produces a StackOverflow exception. > > What is the reason that the second, incorrect definition throws a > StackOverflow exception, but the first generates an infinite sequence? > > Thanks. > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.