Problem
I am trying to define a macro using Clojure that works similarly to try in java It should be able to have a binding form, for example [variable value], that can bind to an instance of a closable.
Example in Java
try (Socket s = new Socket()) {
s.accept();
} catch(Exception e) {}
In this code, the socket 's' is automatically closed as if there is explicitly a finally clause
finally {
if (s != null) s.close();
}
Attempted solution
;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)))
)
)
Example input and output
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)>
Error message
When I put these example inputs into my main function I get a compiler excpetion that I don't understand.
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)
I don't know what I can adjust in this macro but I cannot get it to not return errors.
Update
This solution almost works
(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#)))
))
)
But the following test fails
(defn -main "I don't do a whole lot ... yet." [& args] (import java.io.FileReader java.io.File) (def v (safe [s (FileReader. (File. "file.txt"))] (.read s))) (println v) )
user$ lein run
caught exception: file.txt (No such file or directory)
user$ cat file.txt
teast
with-open
for this. – Taylor Wood(defmacro safe [& [s newS] expression] (if s
(let [~s ~newS]))(with-open ~newS & ~expression) ) (defn -main [& args] (def v (safe (/ 1 0))) )
– Sammacroexpand
gives for your macro? You have the code formatted poorly here, so it's hard to read. – Carcigenicates
andnewS
are for your divide by 0 example. – Carcigenicate