I'd just like to pile-on to say that I ran into the same question last
night in a slightly different form.
I have a file containing a sequence of clojure forms, which I want to
read in sequentially. I don't have to have them all in memory at the
same time for my task. A lazy sequence which yielded parsed forms
while consuming bytes from the underlying file would be a perfect
solution, but I ran into the same issue.
I close the file when EOF is reached. I can (but don't yet, because it
was a quick hack late at night) catch IOExceptions and close the
underlying file under those circumstances, but this is ugly.
What happens if the caller grabs such a lazy sequence and then just
drops it on the floor? Does the file ever get closed before the VM
exits? Do InputStreams register a finalizer?
Needed: a pattern for handing a lazy sequence backed by a closeable resource.
// ben
I think you have two ways to deal with this: (1) Include all your
processing inside the with-open body, or (2) realize the seq by
calling doall.
With (1) you can take advantage of the laziness by using only the
parts of the seq you really need, but you have to have all your
processing in there which may seem a bit messy when there is a lot of
processing code.
With (2) you can return the complete seq for processing in other
places, so you can encapsulate the reading in a function, but you have
all the data in memory.
You could also combine these approaches, by doing some preprocessing
(like dropping 5 elements) and then returning the realized result seq.
(with-open [st (FileInputStream. "msghub.clj")] (doall (drop 5 (byte-seq st))))
I hope this helps
Janico
I suspect that You're Doing it Wrong.
You'll see the exception only if you actually try to evaluate the lazy
sequence returned by byte-seq.
(import [java.io InputStream FileInputStream])
(defn byte-seq
[#^java.io.InputStream stream]
(lazy-seq
(let [b (. stream (read))]
(if (>= b 0)
(cons b (byte-seq stream))))))
(defn most []
(with-open [st (FileInputStream. "/home/smithma/.bashrc")]
(drop 5 (byte-seq st))))
(def x (most)) ;; this is OK, it just binds x to the lazy seq
returned by (most)
(first x) ;; not ok, as it tries to get the first element of x, which dies
;; since the file we're reading from is already closed.