请在 2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面,以了解更多有关此页如何运作的信息。

0 投票
tools.macro
以下是一个片段:


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

(defmacro specialize [type body]
  `(symbol-macrolet [type$ ~(get spec-map type :type)]
     ~(walk-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 在正确代码中处理命名空间的方式是正确的,但它没有做所需的错误处理,因为它允许你使用 qualified symbols 进行 symbol-macrolet。

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

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

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

尽管你可能会更喜欢将其写为

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

但这只是个口味问题。

0 投票
by
...