Kane
Kane

Reputation: 908

What's the return value of this function?

I write this function, but what's its return value.

(defn read-data [file]
  (let [code (subs (.getName file) 0 3)]
    (with-open [rdr (clojure.java.io/reader file)]
      (drop 1 (line-seq rdr)))))

(def d (read-data "data.db"))

It's OK til now. But when I want print this out.

(clojure.pprint/pprint d)

I got a exception:

Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Stream closed

so I confused, what's wrong? The return value isn't a list? How to debug in this situation as an newbie?

Thanks!

Upvotes: 4

Views: 711

Answers (3)

mobyte
mobyte

Reputation: 3752

As example you can use closure and modified "line-seq" for auto close reader:

(defn line-seq2 [^java.io.BufferedReader rdr]
  (if-let [line (.readLine rdr)]
    (cons line (lazy-seq (line-seq2 rdr)))
    (.close rdr)))

(defn my-reader [file]
  (let [lines (line-seq (clojure.java.io/reader file))]
    (fn [] lines)))

(def d (my-reader "data.db"))

now variable "d" is a function:

user> (drop 1 (d))
=> ... file body except first line ...

Upvotes: 3

Jeremy
Jeremy

Reputation: 22415

The problem is that line-seq is lazy and that the reader is closed by the time it is evaluated.

That means all the lines need to be read within the scope of your with-open. One option is to force the full evaluation of line-seq by using doall, as shown below.

(drop 1 (doall (line-seq rdr)))

A potential problem with this approach is that you'll get an OutOfMemoryError if the file is larger than the available memory. So, depending on what you are trying to accomplish, there might be other less memory-intensive solutions.

Upvotes: 4

Ankur
Ankur

Reputation: 33637

(drop 1 (line-seq rdr))

This line returns a sequence i.e a lazy or stream. So basically the file reader rdr is not actually read when this line is executed, but when you try to access this sequence in your print method then the rdr is being read by the line-seq function, but as you are using with-open, that rdr is already closed when the execution flow out of with-open.

Please refer to doall to make a lazy sequence evaluate whenever it is created. I would suggest you read about laziness in FP in general and sequences in particular for Clojure to have a better understanding of lazy evaluation.

Upvotes: 1

Related Questions