2024 Clojure状态调查中分享您的想法!

欢迎!请参阅关于页面以获得更多有关如何使用本指南的信息。

0投票
tools.macro
下面是一个代码片段


(def spec-map
  {:int {:type 'ints}})

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(-> spec-map type :type)]
     ~(w/postwalk
       (fn [form]
        (if-let [tag (-> form meta :tag)]
           (if (= tag 'type$)
             (with-meta form {:tag (-> spec-map type :type)})
             form)
         form))
       (mexpand-all body))))

(defmacro caster [x]
  `(type$ ~x))

(specialize :int
 (defn test-getter [x]
   (let [^type$ x x]
     (prn "我的类型" type$)
     (caster x)
     (aget x 0))))


它在崩溃,错误是“无法解析符号:type$在此上下文”。为什么是这样?让我们查看宏扩展


(clojure.tools.macro/symbol-macrolet
    [clojure.core.matrix.impl.ndarray-magic/type$ ints]
    (def test-getter
     (fn* ([x] (let* [x x] (prn "我的类型" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))


现在,原因很明显:symbol-macrolet期望命名空间符号,并且在扩展形式中,“type$”一次是命名空间,一次不是。我认为symbol-macrolet可以(应该)在此处忽略命名空间。

2个答案

0投票

评论由:khinsen发布

经过一番思考,我认为正确的解决方案是将符号宏像普通符号一样进行评估。这意味着全局定义(defsymbolmacro)使用命名空间符号,而局部符号定义(symbol-macrolet)只能接受普通符号,并且对于有 qualification 符号会抛出异常,就像let 一样。

遵循这些规则,tools.macro在正确代码中对命名空间的处理是正确的,但它没有执行所需的错误处理,因为它允许你使用符号宏来定义符号。

注意,根据这些规则,你的示例代码是不正确的。你必须写成:

(symbol-macrolet [~'type$ ...] ...)

(defmacro caster [x] `(~'type$ ~x))

尽管你可能更喜欢将后者写成:

(defmacro caster [x] (list 'type$ x))

但这只是个人喜好问题。

0投票
参考:[https://clojure.atlassian.net/browse/TMACRO-3](https://clojure.atlassian.net/browse/TMACRO-3)(由si14报告)
...