Clojure try和catch可以在不同(但嵌套)的宏中吗?

Clojure try和catch可以在不同(但嵌套)的宏中吗?,clojure,macros,try-catch,Clojure,Macros,Try Catch,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) (c

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))
但打电话(试试我)会产生:

顺便说一句,这也是在不尝试时使用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
):

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


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

出现此错误的原因是因为
try
的语法是:

(try expr* catch-clause* finally-clause?)
这意味着在catch和finally子句之前可以有任意数量的expr表单
try
扫描
expr
s,直到找到一个以
catch
开始的expr
。它在展开任何宏之前都会这样做,因为它只是试图找出expr和catch/finally子句的起始位置。它收集所有的
catch
finally
子句,并为它们建立适当的错误处理环境

一旦执行此操作,它将正常执行所有
expr
表单。所以它会展开宏,然后执行它们。但是
catch
不是一个函数或特殊形式,它只是
try
在前面的步骤中寻找的东西。因此,当它正常执行时,您会得到与在REPL中键入它时相同的错误


您可能应该编写一个围绕整个代码的宏,该宏将扩展为所需的
try
/
catch
表达式。如果没有一个例子来说明您要实现的目标,就很难给出一个具体的答案。

简短的答案是肯定的,尽管使用特殊形式嵌套宏可能会导致一些类似于此的双重引用难题。必须防止在两个扩展级别对符号进行评估:

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

我目前的猜测是(抓住我)首先被扩展,然后才清楚它是在一次尝试中。是这样吗?如何解决这个问题?宏只在表达式正常求值的地方展开。<代码> catch <代码>子窗体不是被评估的表达式,它们是<代码>语法>尝试> <代码>的一部分。我目前仍然认为这个问题是没有答案的。令人费解的是,为什么Arthur的示例有效,但使用语法引用的示例无效,而且macroexpand all显示了一个有效的宏扩展,但直接调用它会失败并出现错误。感谢Barmar的解决方案。还有其他想法吗?我知道你在说什么,但我觉得问题是因为
捕获
评估得太快而不是太晚,这就是我读你答案的方式,你同意吗?这也解释了为什么@arthur的答案(我同意他的说法,这不是最漂亮的解决方案)是有效的:
catch
是拼接的,但还没有进行评估。我正在努力实现这一点,但仍然使用语法引用:-)我确实遵循了你的建议,并将我所有的代码包装在一个宏中,将发布该宏,或者,如果我发现它,一个更漂亮的解决方案:-)问题是,
catch
根本不应该被评估,没有“太早”
catch
finally
不是真正的运算符,即使它们是按原样编写的。编译器在扫描
try
的主体时会逐字逐句地查找它们。对,但我认为在宏处理过程中,try一开始只是“粘贴”(引用),这可以解释为什么@Arthur的解决方案有效(即使不适用于我)?在任何情况下,
macroexpand all
显示了一个有效的扩展,但它仍然不起作用,这让我感到困惑。我想说这是
macroexpand all
中的一个bug,但它似乎不可修复。
try
正文中的任何内容,如果不是以
catch
finally
开头,则必须进行宏扩展。因此,当它完成时,无法判断这些
catch
子表达式不是原始形式,这就是它们需要识别的地方。不幸的是,
try
的语法像这样模棱两可。我理解你的意思,只是我觉得不对,这也与@Arthur的例子有效的事实相矛盾,不是吗?因为你的建议和
(clojure.walk/macroexpand-all)(try me))
这两种方法都有效,我仍在努力寻找一种使用语法引用的方法。如果我找到它,我会发布,或者只是跟进@Barmar的建议。
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