如果重要的话,我正在使用CIDER。
(defmacro trace [prompt x]
(let [p (subs prompt 4)
expanded (macroexpand x)]
(cond (seq? expanded) `(do (println ~p '~x "...")
(let [result ~(map #(if (or (not (symbol? %)) (function? %))
(list 'trace (join [prompt prompt]) %)
%) expanded)]
(println ~p result "->" ~expanded))
~expanded)
:else expanded)))
这就是我正在研究的宏,但它并不重要(虽然它可能确实如此)。
这是导致问题的特定代码
(trace " " (if true 6 4))
评估此直接引发异常:
Can't let qualified name: clj-match.trace/result
我宏扩展了表单来调试它,我得到了这个:
(do
(println "" '(if true 6 4) "...")
(let* [result (if true 6 4)] (println "" result "->" (if true 6 4)))
(if true 6 4))
这看起来并不坏,所以我尝试评估扩展的表单。令人惊讶的是它有效,评估为6。
为什么会这样?
更重要的是,我得到异常的错误是什么?
答案 0 :(得分:3)
第二个问题:你得到了一个例外,因为你确实试图let
一个合格的名字,特别是clj-match.trace/result
。
这是因为您在(do …)
案例中扩展的语法引用的seq?
形式使用result
作为let
形式的本地绑定的名称它产生。该文字result
符号将由读者进行名称空间限定,因为它出现在语法引用的表单中,因此最终结果将是(let [clj-match.trace/result …] …)
,这是不正确的({{1}的名称绑定不能是名称空间限定的)。您可以使用let
来避免这种情况,或者在语法引用的表单之外明确result#
一个符号,并且不要使用它。
(顺便说一句,您可能希望在扩展中使用语法引用而不是仅引用您的gensym
符号,以确保它实际引用您的宏,而不管扩展发生的上下文如何。)
关于扩展宏的实验 - 以表格作为参数的常规trace
调用将揭示上述内容,因此可能您使用了CIDER或其他Emacs包提供的一些工具来内联扩展它?可能是那个设施在某种程度上是错误的。
答案 1 :(得分:2)
`reader宏将其体内的所有符号扩展为命名空间限定。
[JsonProperty("integer")]
public string Number {get;set;}
user=> (macroexpand `(let [a 0] a))
(let* [user/a 0] user/a)
不是有效的本地绑定。
解决方案是使用`。
的gensym速记功能user/a
人类更难阅读,但实际上会生成有效的代码。