如何在Clojure宏中扩展关键字

时间:2017-01-12 21:44:43

标签: clojure

我有以下宏:

(defmacro my-macro [k]
  `(do
     (def pair
        [
          k
          ~(symbol (str "-" (name k)))]
      )))

...扩展为:

(macroexpand-1 `(my-macro :n/k))

(do (def user/pair [user/k -k]))

...相反,我希望它扩展到

(do (def user/pair [:n/k -k]))

如何让宏保留关键字及其命名空间?

谢谢!

3 个答案:

答案 0 :(得分:1)

修订答案

有两件事让你的问题有些混乱。我早些时候误解了它。

  1. 您应该使用常规单引号'macroexpand-1,而不是背景`。反向标记通常仅用于宏定义中,以描绘一段"模板代码"。
  2. 我刚注意到宏定义中的arg是k,您在示例中使用的关键字是:n/k。这些重复的名称会引起混淆。
  3. 让我们重申一下问题:

    (ns clj.demo)
    (defmacro my-macro [arg]
      `(do
         (def pair
            [
              arg
              ~(symbol (str "-" (name arg)))]
          )))
    (println (macroexpand-1 `(my-macro :n/k)))
    
    ;=> (do (def clj.demo/pair [clj.demo/arg -k]))
    

    因此,我们位于clj.demo命名空间中,该命名空间将应用于符号pairarg。我们需要使用arg替换参数~

    (ns clj.demo)
    (defmacro my-macro [arg]
      `(do
         (def pair
            [
              ~arg
              ~(symbol (str "-" (name arg)))]
          )))
    (println (macroexpand-1 '(my-macro :n/k)))
    
    ;=> (do (def clj.demo/pair [:n/k -k]))
    

    这就是你想要的。

答案 1 :(得分:1)

您可以使用namespacename功能从传入的关键字中提取所需的部分,并根据需要进行组合:

user> (defmacro my-macro [k]
        `(do
           (def pair
             [~(keyword (str (namespace k) "/" (name k)))
              ~(symbol (str "-" (name k)))])))

#'user/my-macro
user> (macroexpand-1 `(my-macro :n/k))
(do (def user/pair [:n/k -k]))

答案 2 :(得分:1)

您需要使用k从语法引用中转义~k

(defmacro my-macro [k]
  `(def ~'pair [~k ~(symbol (str "-" (name k)))]))

我在这里也做了一些其他的改变:

  • 惯用格式。不要将([放在一行的末尾 - 并将)]与他们关闭的表达式放在同一行。
  • do在这里完全是多余的。
  • 如果您希望宏扩展为(def pair ...),则需要

    1. 退出语法引用(~
    2. quote符号pair(即'pair
    3. 将这些放在一起,你有~'pair。你必须这样做的原因是,在Clojure中,`<symbol>被读作(quote <current-namespace>/foo>),其中<current-namespace>代表当前的命名空间。但是def不会使用命名空间的名称。因此~'舞蹈。

      (但你可能想要在pair上进行参数设置...否则,每个命名空间多次使用my-macro并不是很有用。)

总的来说,这似乎是一个非常奇怪的宏。我不知道你想要完成什么,但我可能采取不同的方法。