宏扩展表单评估,但非扩展表单抛出异常

时间:2016-03-12 22:28:38

标签: clojure macros cider

如果重要的话,我正在使用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。

为什么会这样?

更重要的是,我得到异常的错误是什么?

2 个答案:

答案 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

人类更难阅读,但实际上会生成有效的代码。