7

I am using this function to write to a file in Clojure.


(defn writelines [file-path lines]
  (with-open [wtr (clojure.java.io/writer file-path)]
    (doseq [line lines] (.write wtr line))))

But this always generates this error:

IllegalArgumentException No matching method found: write for 
class java.io.BufferedWriter in
clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:79)

What am I doing wrong here?

abc def foo bar
  • 2,360
  • 6
  • 30
  • 41
  • Could you, for added clarity, add an example function call in your question? It is likely to confirm opqdonut's answer. – Gert Dec 19 '11 at 08:42
  • Not an answer to your question, but might be interesting in this context: http://stackoverflow.com/questions/7756909/in-clojure-1-3-how-to-read-and-write-a-file/7757674#7757674 – Michiel Borkent Dec 19 '11 at 16:40

3 Answers3

17

First of all, your function works just fine for many inputs:

Clojure 1.3.0
user=> (defn writelines [file-path lines]
  (with-open [wtr (clojure.java.io/writer file-path)]
    (doseq [line lines] (.write wtr line))))
#'user/writelines
user=> (writelines "foobar" ["a" "b"])
nil
user=> (writelines "quux" [1 2])
nil

However, when you try to pass in something weird we get the error you describe:

user=> (writelines "quux" [#{1}])
IllegalArgumentException No matching method found: write for class  java.io.BufferedWriter  clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:79)

This error is because BufferedWriter has multiple overloaded versions of write and clojure doesn't know which one to call. In this case the conflicting ones are write(char[]) and write(String). With inputs like strings ("a") and integers (1) clojure knew to call the String version of the method, but with something else (e.g. a clojure set, #{1}) clojure couldn't decide.

How about either ensuring that the inputs to writelines are indeed Strings or stringifying them using the str function?

Also, have a look at the spit function.

opqdonut
  • 5,119
  • 22
  • 25
  • Note: If you want to stringify an arbitrarily nested Clojure value, it's better to use `pr-str` rather than `str`. If the resulting string is fed directly to a stream, you should use `pr` (or `prn`) instead of the two-step `pr-str` followed by `print` or `.write`. – raek Dec 19 '11 at 17:02
2

Try this:

(defn writelines [file-path lines]
  (with-open [wtr (clojure.java.io/writer file-path)]
    (binding [*out* wtr]
      (doseq [line lines] (print wtr line)))))

If you look at the documentation for BufferedWriter you'll see no corresponding method to the way you were calling write (whoops, I missed the inherited methods, silly me!). Binding to *out* is just easier all around, I think (unless you also want to be outputting debugging information, in which case it might be a bit trickier).

mange
  • 3,172
  • 18
  • 27
0

Going by the exception message, lines is not a seq of strings, ints (chars) or int arrays.

Joost Diepenmaat
  • 17,633
  • 3
  • 44
  • 53