Reputation: 91534
When I try to add metadata to an infinite lazy sequence in Clojure, I get a stack overflow, and if I take off the metadata, then it works just fine. Why does adding the with-meta
macro break the lazy seq?
First create an infinite seq of a very nice number:
(defn good [] (lazy-seq (cons 42 (good)))) user> (take 5 (good)) (42 42 42 42 42)
Then, add some metadata to each of the lazy-seq instances:
(defn bad [] (lazy-seq (cons 42 (with-meta (bad) {:padding 4})))) user> (take 5 (bad)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Try moving the meta data up one level:
(defn also-bad [] (with-meta (lazy-seq (cons 42 (also-bad))) {:padding 4})) user> (take 5 (foo)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Here is an example of meta data on a finite sequence:
(defn also-works [] (lazy-seq (cons 4 (with-meta () {:a 5})))) user> (also-works) (4) user> (meta (rest (also-works))) {:a 5} user>
Upvotes: 3
Views: 387
Reputation: 72926
Because a LazySeq
evaluates its body as soon as you call withMeta
on the LazySeq
. You lose your laziness.
public final class LazySeq extends Obj implements ISeq, List{
...
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
...
}
seq()
evaluates the lazy seq's body if it hasn't already been evaluated. Your code above keeps calling with-meta
on successive lazy seqs, which evaluates them all until the stack explodes. I don't think there's currently any way to add metadata to a lazy seq without causing it to evaluate its body.
Upvotes: 6