Aieh, I oversimplified a bit here, and forgot about not being able to overload with the same arity, without multimethods.
Here is the actual code I was experimenting with, which actually runs... (defn- read-seq- [reader extract advance] (let [segment (extract reader) reader (advance reader)] (if segment (cons segment (lazy-seq (read-seq- reader extract advance)))))) (defmulti read-seq (fn [source & more] (class source))) (defmethod read-seq clojure.lang.ISeq ([reader] (read-seq reader (fn [reader] (first reader)))) ([reader extract] (read-seq reader extract (fn [reader] (next reader)))) ([reader extract advance] (read-seq- reader extract advance))) (defmethod read-seq java.io.BufferedReader ([reader] (read-seq reader (fn [reader] (. reader readLine)))) ([reader extract] (read-seq reader extract (fn [reader] reader))) ([reader extract advance] (read-seq- reader extract advance))) (defmethod read-seq String [path] (let [reader (clojure.java.io/reader path)] (read-seq reader (fn [reader] (let [segment (. reader readLine)] (if segment segment (. reader close))))))) On Sun, Mar 27, 2011 at 12:17 PM, Stefan Sigurdsson <ste...@gmail.com>wrote: > 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