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