DaoWen
DaoWen

Reputation: 33019

Try-with-resources in Clojure

Does Clojure have an equivalent of Java's try-with-resources construct?

If not, what is the normal way to handle this idiom in Clojure code?

The pre-Java-7 idiom for safely opening and closing resources is verbose enough that they actually added support for try-with-resources to the language. It seems strange to me that I can't find a macro for this use case in the standard Clojure library.

An example to a mainstream Clojure-based project repository—showing how this issue is handled in practice—would be very helpful.

Upvotes: 9

Views: 2415

Answers (2)

erdos
erdos

Reputation: 3528

You can use with-open to bind a resource to a symbol and make sure the resource is closed once the control flow left the block.

The following example is from clojuredocs:

(with-open [r (clojure.java.io/input-stream "myfile.txt")] 
  (loop [c (.read r)] 
    (when (not= c -1)
      (print (char c)) 
      (recur (.read r)))))

It will be expanded to the following:

(let [r (clojure.java.io/input-stream "myfile.txt")] 
  (try
    (loop [c (.read r)] 
      (when (not= c -1)
        (print (char c)) 
        (recur (.read r))))
    (finally (.close r))))

As you can see, a let block is created with a try and finally to the call .close() method.

Upvotes: 16

leetwinski
leetwinski

Reputation: 17859

you can do something more close to java, making up some macro on top of with-open. It could look like this:

(defmacro with-open+ [[var-name resource & clauses] & body]
  (if (seq clauses)
    `(try (with-open [~var-name ~resource] ~@body)
          ~@clauses)
    `(with-open [~var-name ~resource] ~@body)))

so you can pass additional clauses alongside the binding.

(with-open+ [x 111]
  (println "body"))

expands to simple with-open:

(let*
  [x 111]
  (try (do (println "body")) (finally (. x clojure.core/close))))

while additional clauses lead to wrapping the it in try-catch:

(with-open+ [x 111
             (catch RuntimeException ex (println ex))
             (finally (println "finally!"))]
  (println "body"))

expands to

(try
  (let*
    [x 111]
    (try (do (println "body")) (finally (. x clojure.core/close))))
  (catch RuntimeException ex (println ex))
  (finally (println "finally!")))

But still it is quite an opinionated solution.

Upvotes: 1

Related Questions