^:once is absolutely needed for lazy seqs, because it allows the closed-over variables to get cleared while the lambda is
, rather than waiting for it to finish. You can see the difference in this repl session:
user> (let [x (for [n (range)] (make-array Object 10000)), f (^:once fn* [] (nth x 1e6))] (f))
#<Object[] [Ljava.lang.Object;@402d3105>
user> (let [x (for [n (range)] (make-array Object 10000)), f (fn* [] (nth x 1e6))] (f))
; Evaluation aborted.
Of course you may need to tune the numbers to make sure it runs out of space on your machine, but the point is that without ^:once, the lambda f holds onto the head of x, in case f is called again. ^:once constitutes a promise to only ever call it once, and so the sequence is cleaned up as soon as possible. My example doesn't use LazySeq, but I hope you can see how the same considerations apply there.