I have a LazySeq of connections that are created when realized. If an exception occurs while attempting to create a connection, I'd like to iterate through all of the connections that have already been realized in the LazySeq and close them. Something like:
(try
(dorun connections)
(catch ConnectException (close-connections connections)))
This doesn't quite work though since close-connections
will attempt to realize the connections again. I only want to close connections that have been realized, not realize additional connections. Any ideas for doing this?
This returns the previously realized initial fragment of the input seq as a vector:
(defn take-realized [xs]
(letfn [(lazy-seq? [xs]
(instance? clojure.lang.LazySeq xs))]
(loop [xs xs
out []]
(if (or (and (lazy-seq? xs) (not (realized? xs)))
(and (not (lazy-seq? xs)) (empty? xs)))
out
(recur (rest xs) (conj out (first xs)))))))
Testing at the REPL:
(defn lazy-printer [n]
(lazy-seq
(when-not (zero? n)
(println n)
(cons n (lazy-printer (dec n))))))
(take-realized (lazy-printer 10))
;= []
(take-realized (let [xs (lazy-printer 10)] (dorun (take 1 xs)) xs))
;=> 10
;= [10]
;; range returns a lazy seq...
(take-realized (range 20))
;= []
;; ...wrapping a chunked seq
(take-realized (seq (range 40)))
;= [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
; 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]
;; NB. *each* chunk of range gets its own LazySeq wrapper,
;; so that it is possible to work with infinite (or simply huge) ranges
(Using ;=>
to indicate a printout.)
realized?
is indeed the way to go, as suggested by Nathan. However, as I explained in my comments on Nathan's answer, one must also make sure that one doesn't inadvertently call seq
on the one's input, as that would cause the previously-unrealized fragments of the input seq to become realized. That means that functions such as non-empty
and empty?
are out, since they are implemented in terms of seq
.
(In fact, it is fundamentally impossible to tell whether a lazy seq is empty without realizing it.)
Also, while functions like lazify
are useful for unchunking sequences, they do not prevent their underlying seqs from being realized in a chunked fashion; rather, they enable layers of processing (map
, filter
etc.) to operate in an unchunked fashion even while their original input seqs are chunked. There is in fact no connection at all between such "lazified" / "unchunked" seq being realized and its underlying, possibly chunked seq being realized. (In fact there is no way to establish such a connection in the presence of other observers of the input seq; absent other observers, it could be accomplished, but only at the cost of making lazify
considerably more tedious to write.)