6

try位于一个宏中,位于第一个宏catch调用的第二个宏中。如何让以下工作?

(defmacro catch-me []
  `(catch ~'Exception ~'ex
     true))

(defmacro try-me []
  `(try (+ 4 3)
        (catch-me)))

扩展try-me看起来不错:

(clojure.walk/macroexpand-all '(try-me))

产量

(try (clojure.core/+ 4 3) (catch Exception ex true))

但调用 (try-me) 会产生:

"Unable to resolve symbol: catch in this context",

顺便说一句,这也是您在未尝试使用 catch 时会在 REPL 中收到的消息。

更新:

这就是我如何让它工作(感谢@Barmar),在这里你可以看到我的代码的实际上下文:

(defmacro try-me [& body]
  `(try
     ~@body
     ~@(for [[e msg] [[com.mongodb.MongoException$Network "Database unreachable."]
                      [com.mongodb.MongoException "Database problem."]
                      [Exception "Unknown error."]]]
         `(catch ~e ~'ex
            (common/site-layout
             [:div {:id "errormessage"}
              [:p ~msg]
              [:p "Error is: " ~e]
              [:p "Message is " ~'ex]])))))

但这就是我所希望的(使用单独的宏catch-me):

(defmacro try-me [& body]
  `(try
     ~@body
     (catch-me com.mongodb.MongoException$Network "Database unreachable.")
     (catch-me com.mongodb.MongoException "Database problem.")
     (catch-me Exception "Unknown error.")))

我认为这会更容易编写/维护。

有任何想法吗?我需要语法引用,因为我正在传递参数,这就是为什么不幸的是 Arthur 的答案无法应用(或者可以以某种方式应用?),但我直到现在才发布我的实际上下文。

4

2 回答 2

5

您收到该错误的原因是因为 for 的语法try是:

(try expr* catch-clause* finally-clause?)

这意味着在catchfinally子句之前可以有任意数量的expr形式。扫描s 直到找到以or开头的。它扩展任何宏之前执行此操作,因为它只是试图找出 expr 和 catch/finally 子句从哪里开始。它收集所有的and子句并为它们建立适当的错误处理环境。tryexprcatchfinallycatchfinally

一旦这样做,它就会expr正常执行所有表单。所以它扩展了它们的宏,然后执行它们。但catch不是函数或特殊形式,它只是try在前面的步骤中寻找的东西。所以当它正常执行时,你会得到与在 REPL 中键入它时相同的错误。

您可能应该做的是编写一个宏,将其包裹在整个代码中,该代码扩展为您想要的try/catch表达式。如果没有您要完成的工作的示例,很难给出具体的答案。

于 2012-10-29T22:35:05.053 回答
1

简短的回答是肯定的,尽管嵌套具有特殊形式的宏可能会导致一些像这样的双引号问题。必须防止符号在两个扩展级别上被评估:

user> (defmacro catch-me []                                   
          '(list 'catch 'Exception 'ex  
                       'true))

user> (defmacro try-me []
    `(try (+ 4 3)              
                  ~(catch-me)))
#'user/try-me

user> (try-me)
7

并查看它是否也捕获了异常:

user> (defmacro try-me []
    `(try (/ 4 0)
              ~(catch-me)))
#'user/try-me
user> (try-me)
true
于 2012-10-29T22:38:55.157 回答