Clojure: try, catch macro that also closes any fil

2020-03-27 05:48发布

问题:

UPDATE:

Thanks for all the help so far. This is my new code, which works. But not exactly as I would like it to.
I need it to return the java exception (don't ask me why). Example:

(safe (/ 1 0))
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>

which is how I want it to be. But when I make use of the other clauses of code that handle bindings etc, I get the Clojure exception:

(seefe [f(FileReader.(File. "C:/txtf.txt"))](. f read))
FileNotFoundException C:\txtf.txt (The system cannot find the file specified)  java.io.FileInputStream.open (:-2)

What can I do to prevent this, and show the Java exception instead?


(defmacro safe [bindings & code]
  (if (list? bindings)
    `(try 
      (println "line 8: try")
      ~bindings
      (catch Throwable e# e#))

  (if (= (count bindings) 0)
     `(try ~code
           (catch Throwable e# e#))
     `(let ~(subvec bindings 0 2)
                              (try
                                (safe ~(subvec bindings 2) ~@code)
                                (catch Throwable e# e#)
                                (finally
                                 (. ~(bindings 0) close)))))))

OLD

I'm trying to get an assignment done, but it's impossible without any tutoring. My teachers expect us to teach ourselves Clojure in 1 week and complete this assignment. Everyone in my class is stuck and we already hate the teacher, lmao.

Okay, so the macro is supposed to be able to try code, and return a result or an exception. It's supposed to be able to handle expressions like:

(def v (safe [s (FileReader. (File. "file.txt"))] (. s read)))

If the code opened any filestreams or whatnot, it should close them in a finally clause. This is what I got so far - I realize it's not working.

(defmacro safe [& body]
`(try ~@body 
 (catch Throwable e# e#)
 (finally 
   (if (. ~@body isInstance Closable) (. ~@body close)))))

The error I get is:

Unable to resolve symbol: s in this context, compiling:(NO_SOURCE_PATH:1)

I got desperate so I tried alot of different stuff, I tried:

to edit the macro:

(defmacro safe [& body]
`(try ~@body 
      (catch Throwable e# e#)
      (finally (if (. ~@body isInstance Closable) (. ~@body close)))))

then run:

(safe (. (java.io.FileReader. (java.io.File. "C:/Users/Dyallo.L/Dropbox/DVK11/PROP/Clojure/txt.txt")) read))

Which resulted in:

No such var: clooj.cemerick.pomegranate/Closable, compiling:(NO_SOURCE_PATH:1)

Someone mentioned the macro WITH-OPEN but I guess that won't work well with my generic macro. The macro isn't meant for opening files, but if they are, it should definately close them.

So darn, won't you give me a hand Stackoverflow-geniuses? Thanks in beforehand.

回答1:

from the example it looks like the first form passed to the safe macro is a vector of name expression pairs [a (open-something "/foo/bar") b (oopen-something-else)] followed be some number of expressions that use these symbols. if I'm interpreting the assignment correctly the result would be quite similar to with-open and the various with-connection macros. it's worth double checking this assumption before submitting your results of course. It would be much more difficult if the macro was supposed to find closable values in its body with out at least some knowledge of its structure.



回答2:

with-open macro does exactly the same thing.

If there is variable (can be more than one) bound to created object it wraps the body to try-catch block with finallyclosing.

(macroexpand-1 '(with-open [f (FileReader. (File. "1.txt"))]
                  (do this with f)
                  (do that with f)))

=>
(clojure.core/let
 [f (FileReader. (File. "1.txt"))]
 (try (clojure.core/with-open []
        (do this with f)
        (do that with f))
      (finally (. f clojure.core/close))))

If there are no bindings then it just returns the body

(macroexpand-1 '(with-open []
                  (do this with f)
                  (do that with f)))

=>
(do (do this with f)
    (do that with f))

Update. Arthur has already explained "exception" part of your question.