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 "my type" 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 "my type" type$) (clojure.core.matrix.impl.ndarray-magic/type$ x) (aget x 0))))))


现在,原因很明显:symbol-macrolet 预期的是命名空间中的符号,在展开形式中 "type$" 一次是命名空间中的,一次不是。我认为 symbol-macrolet 可以(应该?)在这里忽略命名空间。

2 个答案

0

评论者:khinsen

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

有了这些规则,tools.macro 在正确代码中对命名空间的处理是正确的,但它未进行必要的错误处理,因为它允许使用限定符号的 symbol-macrolet。

请注意,根据这些规则,您的示例代码是不正确的。您必须编写

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

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

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

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

但这是一个口味问题。

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