Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 476
DaoWen Avatar asked Oct 10 '17 15:10

DaoWen


People also ask

How do you use try with resources?

In Java, the try-with-resources statement is a try statement that declares one or more resources. The resource is as an object that must be closed after finishing the program. The try-with-resources statement ensures that each resource is closed at the end of the statement execution.

Can we use try with resources without catch and finally?

Yes, It is possible to have a try block without a catch block by using a final block. As we know, a final block will always execute even there is an exception occurred in a try block, except System. exit() it will execute always.

Can multiple resources be used in try with resources?

We can declare multiple resources in a try block. Try initialization block can have any number of resources resulting in either null or non-null resources. In the below example, we can able to declare multiple resources in the try-with-resources statement.

Does try with resources automatically close?

The Java try with resources construct, AKA Java try-with-resources, is an exception handling mechanism that can automatically close resources like a Java InputStream or a JDBC Connection when you are done with them. To do so, you must open and use the resource within a Java try-with-resources block.


2 Answers

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)))))

This 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))))

You can see that a let block is created with a try-finally to the call .close() method.

like image 191
erdos Avatar answered Sep 20 '22 02:09

erdos


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.

like image 28
leetwinski Avatar answered Sep 22 '22 02:09

leetwinski