Hi all, I've been trying to incorporate lazy seqs into my project, but kept running into troubles where things were evaluated that I didn't think should be. After thinking a bit and looking into the Clojure source for the first time, I think I've realized the source of the issue: the empty seq == nil. This means that every "lazy" function or macro that returns a seq -- for, lazy-cat, filter, etc. -- must always do enough pre-emptive evaluation to determine whether the return value should be nil or an object. So, my first question is, is this analysis correct? If not, please enlighten me (and either way, maybe consider devoting a FAQ question to this...).
Now, assuming this is correct, my follow-up question is: why do things this way (rather than using "empty?" to tell if a seq is empty, like a collection)? I'm very new to the community and don't mean to suggest that I know better, I'm just curious about the rationale since I'm sure it will shed more light on the language for me. Finally, the workaround. A little background: I was trying to build a lazy stack data structure (where entire lazy seqs, incidentally involving a filter or (for :when) could be pushed on the front), and found that elements kept being unexpectedly evaluated. In particular, any element ever at the top of the stack was always evaluated, even if it was never popped and was subsequently buried by other elements. I wanted to avoid this extra evaluation, without introducing "delay" and "force" everywhere, or writing my own versions of the built-in seq functions. To this end, I designed a new Java class that creates a truly lazy *collection* from a seq-able expression. Since collections don't have the same contract as seqs, it does not need to preemptively evaluate arguments to determine if it is empty, and won't do so until actually needed (e.g., you call seq on the collection). You can download the Java class [1], and use it with the following Clojure wrapper: (defmacro delay-seq "Create a collection representing this seq-able expr, *really* without evaluating it until needed." [expr] `(DelayedSeq. (fn [] (seq ~expr)))) Then, if you want an operation to be *really* lazy, you just wrap it with a call to delay-seq. For instance, (delay-seq (concat .....)) will never evaluate the concat call until actually needed. Note that the result of delay-seq is a seq-able collection, and no corresponding force call is ever needed. [1] http://w01fe.com/?attachment_id=48 Comments about this are very welcome; this was my first voyage into the internals of Clojure, and I still have lots to learn. Cheers, Jason --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---
