宏展开形式求值,但非展开形式抛出异常
Macroexpanded form evaluates, but non-expanded form throws exception
如果这很重要,我正在使用 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.
为什么会这样?
更重要的是,我做错了什么以获得异常?
首先是第二个问题:您遇到了一个异常,因为您确实在尝试 let
一个合格的名称,特别是 clj-match.trace/result
.
发生这种情况是因为宏在 seq?
情况下扩展成的语法引用 (do …)
形式使用 result
作为 [=10= 中本地绑定的名称] 它产生的形式。文字 result
符号将由 reader 进行命名空间限定,因为它出现在语法引用形式内,因此最终结果将是 (let [clj-match.trace/result …] …)
,这是不正确的(名称let
绑定不能是命名空间限定的)。您可以使用 result#
来避免这种情况,或者明确地 gensym
语法引用形式之外的符号并取消引用以使用它。
(顺便说一句,您可能希望在扩展中语法引用您的 trace
符号,而不仅仅是引用,以确保它实际上是指您的宏,而不管扩展所在的上下文发生。)
至于您扩展宏的实验——以您的表单作为参数的常规 macroexpand-1
调用将显示上述内容,因此大概您使用了 CIDER 或其他 Emacs 包提供的一些工具来内联扩展它?可能那个设施有点问题。
`reader 宏将其主体内的所有符号扩展为命名空间限定。
user=> (macroexpand `(let [a 0] a))
(let* [user/a 0] user/a)
user/a
不是有效的本地绑定。
解决方案是使用 `.
的 gensym shorthand 特性
user=> (macroexpand `(let [a# 0] a#))
(let* [a__3__auto__ 0] a__3__auto__)
这对人类来说更难阅读,但实际上生成了有效代码。
如果这很重要,我正在使用 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.
为什么会这样?
更重要的是,我做错了什么以获得异常?
首先是第二个问题:您遇到了一个异常,因为您确实在尝试 let
一个合格的名称,特别是 clj-match.trace/result
.
发生这种情况是因为宏在 seq?
情况下扩展成的语法引用 (do …)
形式使用 result
作为 [=10= 中本地绑定的名称] 它产生的形式。文字 result
符号将由 reader 进行命名空间限定,因为它出现在语法引用形式内,因此最终结果将是 (let [clj-match.trace/result …] …)
,这是不正确的(名称let
绑定不能是命名空间限定的)。您可以使用 result#
来避免这种情况,或者明确地 gensym
语法引用形式之外的符号并取消引用以使用它。
(顺便说一句,您可能希望在扩展中语法引用您的 trace
符号,而不仅仅是引用,以确保它实际上是指您的宏,而不管扩展所在的上下文发生。)
至于您扩展宏的实验——以您的表单作为参数的常规 macroexpand-1
调用将显示上述内容,因此大概您使用了 CIDER 或其他 Emacs 包提供的一些工具来内联扩展它?可能那个设施有点问题。
`reader 宏将其主体内的所有符号扩展为命名空间限定。
user=> (macroexpand `(let [a 0] a))
(let* [user/a 0] user/a)
user/a
不是有效的本地绑定。
解决方案是使用 `.
的 gensym shorthand 特性user=> (macroexpand `(let [a# 0] a#))
(let* [a__3__auto__ 0] a__3__auto__)
这对人类来说更难阅读,但实际上生成了有效代码。