Clojure宏与try块错误:无法绑定限定名称

时间:2017-12-14 21:37:42

标签: clojure macros

我正在尝试定义一个宏来编写代码,以便为传入的表达式执行try catch块。

(defmacro safe
     [expression]
     `(try
          ~expression 
          (catch Exception e (str "caught exception: " (.getMessage e)))
      )
)

宏写得很好但是当我对它运行一些测试时,我收到以下编译器错误

seminar.core=> (def v (safe (/ 1 0)))
CompilerException java.lang.RuntimeException: Can't bind qualified name:seminar.core/e, compiling:(/private/var/folders/6f/q7lhngtn45q_xpzd_24gjp2h0000gn/T/form-init2350735096437822603.clj:1:8) 

seminar.core=> (def v (safe (/ 10 2)))
CompilerException java.lang.RuntimeException: Can't bind qualified name:seminar.core/e, compiling:(/private/var/folders/6f/q7lhngtn45q_xpzd_24gjp2h0000gn/T/form-init2350735096437822603.clj:1:8) 

这就是宏的行为方式

user> (def v (safe (/ 1 0)))
user> v
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>
user> (def v (safe (/ 10 2)))
user> v
5

2 个答案:

答案 0 :(得分:1)

您需要在宏中为e生成唯一符号:

(defmacro safe
  [expression]
  `(try ~expression
     (catch Exception e#
       (str "caught exception: " (.getMessage e#)))))

然后你得到了理想的行为:

(safe (/ 1 0))
=> "caught exception: Divide by zero"

请参阅this相关问题。 #后缀是gensym的特殊简写。这是必要的一个原因是,当扩展宏时,宏中的绑定不会掩盖其他相同名称的绑定。

答案 1 :(得分:0)

最好扩展宏以使用任意数量的表达式返回最后一个作为正结果的表达式。我的版本是:

(defmacro safe [& body]
  `(try
     (do ~@body)
     (catch Exception e#
       (println (format "Error: %s" (.getMessage e#))))))

用法示例:

(safe
 (+ 1 2)
 (println "hey")
 (/ 42 0))

它会在导致错误的那些表单之前评估所有表单。在控制台中打印错误消息时,结果将为nil值:

hey
Error: Divide by zero
nil