【问题标题】:Clojure macro: getting syntax-quoted/namespace-qualified symbolClojure 宏:获取语法引用/命名空间限定符号
【发布时间】:2012-11-06 04:46:20
【问题描述】:

假设我想定义一个名为 defsomething 的宏,这样:

(defspecial a x)

…扩展为:

(def a (f `a x))

语法引用`a 有一个棘手的部分。我看不出如何正确地将当前命名空间附加到符号上,使其表现得像语法引用。例如:

(defmacro defspecial [var-symbol expr]
  `(def ~var-symbol (f '~var-symbol ~expr)))

(defspecial a x)

…扩展为:

(def a (f 'a x))

…很接近,但第二个 a 不是命名空间限定的。

我知道我可以使用*ns*

(defmacro defspecial [var-symbol expr]
  `(def ~var-symbol (f (symbol (name (ns-name *ns*)) (name '~var-symbol)) ~expr)))

……但我不喜欢那样;不仅丑,*ns* 是可重新绑定的,所以这是可能的:

(binding [*ns* (the-ns 'clojure.core)]
  (defspecial a 5))

不幸的是,Clojure 显然没有syntax-quote` 形式,就像它有quote' 一样。那么,我应该如何最好地实现这个宏呢?

【问题讨论】:

  • ` 不附加当前命名空间。如果没有给出你想要的例子以及它为什么不起作用的例子,很难理解你想要实现的目标(我的意思是,我知道你想在某处放置一个语法引用,我在说关于你的宏实际上应该做什么)。
  • 背景是,这是一个将函数组合在一起制作解析器的库。一个更高级别的函数(称为lbl)接受一个函数(称为p)并将其包装在另一个函数中,该函数为p返回的任何内容添加一种“标签”(用于解析函数的语法错误处理)。我想制作一个def 的版本来定义解析器函数,我希望它自动调用函数上的lbl 并用它们的变量名称标记它们。命名空间限定在这里会很好,因为我预计会有很多标签冲突。

标签: clojure macros


【解决方案1】:

你真的试过你给出的最后一个例子吗?编译器对*ns* 的绑定发生在编译时,宏扩展也是如此,因此您所做的运行时绑定应该对defspecial 的行为没有影响。

【讨论】:

  • 嗯!你是对的。我应该事先测试过。看起来这就是答案,虽然它对我的口味来说仍然有点难看,叹息。不过非常感谢!
【解决方案2】:

这是适用于 Clojure 和 ClojureScript 的版本:

(defn qualify-sym
  [env s]
  (if (:ns env)
    ;; cljs
    (symbol (name (-> env :ns :name)) (name s))

    ;; clj
    (symbol (str *ns*) (str s))))

(defmacro example [name val]
  `(def ~name {:id '~(qualify-sym &env name) :value ~val}))

(example xxx 123)

xxx
;;=>
{:value 123, :id my-ns/xxx}

如果您还需要考虑别名和引用变量,那么这是一个更完整的版本:

(defn qualify-sym [env s]
  (if (:ns env)
    ;; cljs
    (if (simple-symbol? s)
      (or (some-> env :ns :uses s name (symbol (name s)))
          (symbol (name (-> env :ns :name)) (name s)))
      (symbol (or (some-> env :ns :requires (get (symbol (namespace s))) name)
                  (namespace s))
              (name s)))

    ;; clj
    (if (simple-symbol? s)
      (or (some-> (ns-refers *ns*) (get s) symbol)
          (symbol (str *ns*) (str s)))
      (let [ns (namespace s)
            n (name s)
            aliases (ns-aliases *ns*)]
        (symbol (or (some-> aliases (get (symbol ns)) ns-name str) ns) n)))))

例如如果你有(:require [clojure.string :as str]),那么符合条件的str/join 将返回clojure.string/join

【讨论】:

    猜你喜欢
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 2011-03-25
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多