Clojure 我可以不使用eval编写此宏吗?
我正在尝试编写一个宏,它将捕获Clojure中的编译时错误。具体地说,我希望捕获在调用尚未针对该数据类型实现的协议方法并抛出Clojure 我可以不使用eval编写此宏吗?,clojure,macros,lisp,eval,Clojure,Macros,Lisp,Eval,我正在尝试编写一个宏,它将捕获Clojure中的编译时错误。具体地说,我希望捕获在调用尚未针对该数据类型实现的协议方法并抛出clojure.lang.Compiler$CompilerException时抛出的异常 到目前为止,我已经: (反宏捕获编译器错误 [正文] (试试看 (评估机构) (捕捉异常e))) 但当然,我被告知,eval是邪恶的,你通常不需要使用它。有没有一种方法可以在不使用eval的情况下实现此功能 我倾向于认为eval在这里是合适的,因为我特别希望在运行时而不是编译时对代码
clojure.lang.Compiler$CompilerException
时抛出的异常
到目前为止,我已经:
(反宏捕获编译器错误
[正文]
(试试看
(评估机构)
(捕捉异常e)))
但当然,我被告知,eval
是邪恶的,你通常不需要使用它。有没有一种方法可以在不使用eval
的情况下实现此功能
我倾向于认为
eval
在这里是合适的,因为我特别希望在运行时而不是编译时对代码进行评估。宏在编译时展开。他们不需要eval
code;相反,它们组装代码,稍后将在运行时进行评估。换句话说,如果要确保传递给宏的代码是在运行时而不是在编译时计算的,这就告诉您绝对不应该在宏定义中计算它
名称catch编译器错误
有点用词不当;如果调用宏的代码有编译器错误(可能缺少括号),则宏实际上无法捕获它。您可以编写如下的catch runtime error
宏:
(defmacro捕获运行时错误
[&正文]
`(试试看
~@体
(捕捉例外e#
e#))
以下是此宏的工作原理:
body
的序列中try
catch
java.lang.Exception
(Exception
的限定版本)e#
(宏扩展)
“(捕获运行时错误
(/ 4 2)
(/ 1 0)))
如您所见,我不是简单地以宏作为第一个元素来评估表单;这将扩展宏并计算结果。我只想执行扩展步骤,因此我使用了macroexpand
,这给了我以下信息:
(试试看)
(/ 4 2)
(/ 1 0)
(catch java.lang.Exception e__19785__自动__
e_uuu19785_uuu自动_uuuu)
这确实是我们所期望的:一个包含符号try
、我们的身体表达式的列表,另一个包含符号catch
和java.lang.Exception
的列表,后面是唯一符号的两个副本
您可以通过直接计算宏来检查该宏是否执行了您希望它执行的操作:
(捕获运行时错误(/42)(/10))
;=> #错误{
;原因“被零除”
通过
;[{:键入java.lang.arithmetricException
;:消息“除以零”
;:在[clojure.lang.Numbers除以“Numbers.java”158]]
痕迹
;[[clojure.lang.Numbers除以“Numbers.java”158]
;[clojure.lang.Numbers除以“Numbers.java”3808]
; ,,,]}
好极了。让我们用一些协议来试试:
(def协议Foo)
(福[这个])
(除协议栏外)
(除此之外)
(数据记录Baz[]
福
(foo[uux]:qux)
(捕获运行时错误(foo(->Baz)))
;=> :库克斯
(捕捉运行时错误(条形图(->Baz)))
;=> #错误{,,}
但是,如上所述,使用这样的宏无法捕获编译器错误。您可以编写一个宏,返回一段代码,对传入的其余代码调用eval
,从而将编译时间推回到运行时:
(defmacro捕捉错误
[&正文]
`(试试看
(eval'(do~@body))
(捕捉例外e#
e#))
让我们测试宏扩展以确保其正常工作:
(宏扩展)
"(捕捉错误)
(foo(->Baz))
(foo(->Baz)无)
这扩展到:
(试试看)
(clojure.core/eval)
"(做)
(foo(->Baz))
(foo(->Baz)无)
(catch java.lang.Exception e__20408__自动__
e_uuu20408_uuu自动_uuuu)
现在,我们可以捕获更多错误,例如由于尝试传递错误数量的参数而导致的IllegalArgumentException
s:
(捕捉错误(条(->Baz)))
;=> #错误{,,}
(捕捉错误(foo(->Baz)nil))
;=> #错误{,,}
但是(我想说得很清楚),不要这样做。如果你发现自己把编译时间推回到运行时只是为了捕捉这些错误,那么你几乎肯定是做错了什么。你最好重组你的项目,这样你就不必这么做了
我猜你已经看到了,这很好地解释了
eval
的一些陷阱。特别是在Clojure中,除非您完全理解它在范围和上下文方面提出的问题,以及该问题中讨论的其他问题,否则您绝对不应该使用它。谢谢!这适用于我预期的应用程序,但是,我注意到,如果我将错误数量的参数传递给方法调用,它将无法捕获错误。相反,它抛出一个非法参数异常。知道为什么吗?@MONODA43您无法捕获该错误,因为它是在编译时抛出的。使用类似这样的try
-catch
可以在Clojure中捕捉到的唯一错误是运行时错误。这现在是有意义的。之所以要这样做,是因为我正在尝试创建一个工具,该工具通过编程方式确定哪些协议由给定的core.matrix实现实现。基本上是为了评估上述实施的完整性。你看到了吗?信息技术