我正在尝试使用Clojure定义一个宏,它的工作方式类似于在java中尝试 它应该能够有一个绑定表单,例如[变量值],它可以绑定到一个可关闭的实例。
try (Socket s = new Socket()) {
s.accept();
} catch(Exception e) {}
在此代码中,套接字'自动关闭,就好像有明确的finally子句
一样finally {
if (s != null) s.close();
}
;I am defining my macro. It can either take one argument(expression) or can take two arguments(expression and a vector with two elements(a variable and value)
;expression is a form expression and should be able to be evaluated
(defmacro safe
; if a vector and also an expression is passed into the macro
[s NewS expression]
;I am defining my try block, and unquoting it with a ' so that the try block is not executed within the macro
`(try
;I am letting the variable(NewS) be equal to the value(s)
(let [s NewS]
;I am trying the expression to see if it is valid and printing the exception if there is one
(try
~expression (catch Exception e (str "caught exception: " (.getMessage e))))
)
;I am checking if my value is an instance of a java closeable
(instance? java.util.Closeable s)
;I am catching the exception from the let statement if there is one
(catch Exception e (str "caught exception: " (.getMessage e)))
)
;if only an expression is passed into the macro
[expression]
`(try
~expression (catch Exception e (str "caught exception: " (.getMessage e)))
)
)
user> (def v (safe (/ 1 0)))
user> v
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>
user> (def v (safe (/ 10 2)))
user> v
5
user> (def v (safe [s (FileReader. (File. "file.txt"))] (.read s)))
user> v
105 ; first byte of file file.txt
user> (def v (safe [s (FileReader. (File. "missing-file"))] (. s read)))
user> v
#<FileNotFoundException java.io.FileNotFoundException:
missing-file (No such file or directory)>
当我将这些示例输入放入我的main函数时,我得到了一个我不理解的编译器激活。
CompilerException clojure.lang.ArityException: Wrong number of args (1) passed to: core/safe, compiling:(/private/var/folders/6f/q7lhngtn45q_xpzd_24gjp2h0000gn/T/form-init2350735096437822603.clj:1:8)
我不知道我可以在这个宏中调整什么,但我不能让它不返回错误。
此解决方案几乎可以使用
(defmacro safe
([[s NewS] expression]
`(try
(let [~s ~NewS] ~expression) (catch Exception e# (str "caught exception: " (.getMessage e#)))
))
([expression]
`(try
~expression (catch Exception e# (str "caught exception: " (.getMessage e#)))
))
)
但以下测试失败
(defn -main &#34;我做不了很多......但是。#34; [&安培;参数] (import java.io.FileReader java.io.File) (def v(safe [s(FileReader。(File。&#34; file.txt&#34;))](。read s))) (println v) )
user$ lein run
caught exception: file.txt (No such file or directory)
user$ cat file.txt
teast
答案 0 :(得分:1)
自动关闭行为已由with-open
宏解决。请参阅实施here。这是(我认为)按照你的要求做出的一种表述。
(defmacro safe
([body]
`(try ~body
(catch Exception e#
(str "caught exception: " e#))))
([bindings & body]
`(try
(with-open ~bindings
~@body)
(catch Exception e#
(str "caught exception: " e#)))))
使用示例:
(safe (/ 1 nil))
;;=> "caught exception: java.lang.NullPointerException"
(safe [s (clojure.java.io/reader "file.txt")]
(prn (.read s)))
;;=> "caught exception: java.io.FileNotFoundException: file.txt (No such file or directory)"
(spit "file.txt" "contents here")
(safe [s (clojure.java.io/reader "file.txt")]
(.read s))
;;=> 99
str
替换为println
块中的catch
。)safe
的灵魂(一个只接受一个身体形态,另一个接受绑定为close
d 和身体形态)的问题令人担忧可以说是分开的。 with-open
已经存在,当我们可以重复使用时,我们不应该重新发明它。这个版本的safe
可以使用任意数量的表单而不只是一个,所以它现在更加灵活。
(defmacro safe
[& body]
`(try ~@body
(catch Exception e#
(println "caught exception:" (.getMessage e#)))))
我们可以轻松使用 with-open
中的safe
来获得您想要的行为:
(safe
(prn (/ 1 2)) ;; prints 1/2
(with-open [s (clojure.java.io/reader "not_a_file.txt")]
(.read s))) ;; fails, prints exception
(safe
(with-open [s (clojure.java.io/reader "file.txt")]
(char (.read s)))) ;; returns the first char from file we `spit` above
;;=> \c