1

如果这很重要,我正在使用苹果酒。

(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。

为什么会这样?

更重要的是,我做错了什么来获得异常?

4

2 回答 2

3

第二个问题首先:您遇到了一个例外,因为您确实在尝试let使用限定名称,特别是clj-match.trace/result.

发生这种情况是因为宏在这种情况下(do …)扩展为的语法引用形式用作它生成的形式中的本地绑定的名称。该文字符号将由阅读器进行命名空间限定,因为它出现在语法引用的形式中,因此最终结果将是,这是不正确的(绑定的名称不能是命名空间限定的)。您可以使用来避免这种情况,或者在语法引用形式之外显式使用符号并取消引用以使用它。seq?resultletresult(let [clj-match.trace/result …] …)letresult#gensym

(顺便说一句,您可能希望在扩展中对符号进行语法引用,而不仅仅是引用,trace以确保它实际上引用了您的宏,而不管扩展发生的上下文如何。)

至于你扩展宏的实验——macroexpand-1以你的表单作为参数的常规调用将显示上述内容,所以大概你使用了 CIDER 或其他 Emacs 包提供的一些工具来内联扩展它?可能是那个设施在某种程度上是错误的。

于 2016-03-12T23:58:13.447 回答
2

` reader 宏将其主体内的所有符号扩展为命名空间限定的。

user=> (macroexpand `(let [a 0] a))
(let* [user/a 0] user/a)

user/a不是有效的本地绑定。

解决方案是使用 `.

user=> (macroexpand `(let [a# 0] a#))
(let* [a__3__auto__ 0] a__3__auto__)

这对于人类来说更难阅读,但实际上会生成有效代码。

于 2016-03-12T23:58:54.343 回答