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

Sam*_*Sam 1 macros clojure

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

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

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

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) 
Run Code Online (Sandbox Code Playgroud)

这就是宏的行为方式

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

Tay*_*ood 5

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

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

然后你就得到了想要的行为:

(safe (/ 1 0))
=> "caught exception: Divide by zero"
Run Code Online (Sandbox Code Playgroud)

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