How do you guys normally manage resources when using lazy sequences?

I was playing around with this question, and I looked into extending
line-seq to take a file path parameter that is coerced to reader:

    (defn line-seq
        ([^java.io.BufferedReader rdr]
            (when-let [line (.readLine rdr)]
                (cons line (lazy-seq (line-seq rdr)))))
        ([^String path]
            (line-seq (clojure.java.io/reader path))))   ;   Oops

This sort of works, but not well, because we're relying on finalize to close
the reader.

(If the extended line-seq is being used by code that opens a lot of files,
but reads all it needs from each file before moving on, then we'd only have
one live file handle at any given time, but we could exhaust the number of
concurrently available file handles before the garbage collector catches
up.)

Instead the extended line-seq needs to control the closing of the reader it
opens, but that reader needs to remain open until the last sequence element
has been consed - which is normally long after the extended line-seq
returns.

The normal Clojure-style with-open approach doesn't work at all since the
reader is closed after reading the first line, and we can't read any of the
remaining lines:

    (defn line-seq
        ([^java.io.BufferedReader rdr]
            (when-let [line (.readLine rdr)]
                (cons line (lazy-seq (line-seq rdr))))))
        ([^String filename]
            (with-open [rdr (clojure.java.io/reader filename)]   ;   Oops...
                (line-seq rdr))))

Closing the reader explicitly when the file has been read fixes the extended
line-seq but would
 break other applications that rely on the original line-seq:

    (defn line-seq
        ([^java.io.BufferedReader rdr]
            (if-let [line (.readLine rdr)]
                (cons line (lazy-seq (line-seq rdr)))
                (.close rdr)))   ;   Ouch
        ([^String filename]
            (line-seq (clojure.java.io/reader filename)))

Adding another overload seems to be the most reasonable solution:

    (defn line-seq
        ([^java.io.BufferedReader rdr]
            (line-seq rdr (fn [rdr] (.readLine rdr))))
        ([^java.io.BufferedReader rdr extract]
            (when-let [line (extract rdr)]
                (cons line (lazy-seq (line-seq rdr)))))
        ([^String filename]
            (let [extract (fn [rdr] (if-let [line (.readLine rdr)] line
(.close rdr))
                (line-seq (clojure.java.io/reader filename) extract)))

But this feels a little cumbersome. Am I missing something? Is there a
better way?

(I'm just using line-seq for illustration here.)

Cheers,

Stefan

On Fri, Mar 25, 2011 at 10:22 AM, Jules <jules.gosn...@gmail.com> wrote:
yes

and that's great where the resource usage is scoped on a per-thread basis,
but not a per-object basis - but then, I am thinking in OO terms again :-)

Jules

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to