Clojure - 宏中的 let 不起作用

Posted

技术标签:

【中文标题】Clojure - 宏中的 let 不起作用【英文标题】:Clojure - a let in a macro won't work 【发布时间】:2013-02-13 19:59:43 【问题描述】:

我创建了一个宏,它创建了一个名为 dispatcher 的宏,它具有 3 个关联函数 get-dispatcherset-dispatchercall-dispatcher,以与调度程序一起工作(它们获得一个调度函数,添加一个或调用一个)。一切正常!但是,现在我想自动创建相关的函数名称,因此我将宏的所有这些内部结构放入定义了该简单构造函数的 let 中。请注意,在下面的代码中,只有 get- 函数的名称是使用该自动化构造的。 set-call- 的名称创建仍然具有手动气味。

(defmacro create-dispatcher [name]
  ;creates a set of dispatching functions tagged

  `(do
    ;define dispatcher
    (def ~(symbol name) ~(atom ))

    (let
      [name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
        ; -- define getter
        (defn (name-w-prefix "get")
          "get-dispatcher [tag]: get a dispatcher fn by tag"
          (~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
          (~'[tag]
            (do
              (println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
              ; return the tagged dispatcher
              ( (keyword ~'tag) @~(symbol name) )))

        )
        ; -- define caller
        (defn ~(symbol (str "call-" name))
          "get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
          ~'[tag & args]
          (apply (~(symbol (str "get-" name)) ~'tag) ~'args)
          )
        ; -- define setter
        (defn ~(symbol (str "set-" name))
          ~'[tag fn]
          "add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
          (swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
          )
     )

    ; -- report
    (println "created dispatcher set for '" ~(str name) "' ok!")
    ))

但是,有一个问题。 let 语句绑定中的 name-w-prefix 会导致错误。我该如何解决?

(也欢迎任何关于改进的建议,因为我是新手,这几乎是我在 Clojure 中写的第一件事)

【问题讨论】:

【参考方案1】:

宏中的所有符号都在当前命名空间中解析,并期望计算为 var。您可以引用 name-w-prefix 符号,但这可能会与宏扩展期间传入宏的符号发生冲突。因此,Clojure 提供了一种特殊的语法,用于在语法引用形式中生成符号 - 只需将 # 附加到符号末尾,Clojure 就会将其视为带引号的自动生成符号。因此,在这种情况下,将出现的 name-w-prefix 替换为 name-w-prefix# 就可以了。

退后一步,看看你的总体目标是什么,我认为你应该将name-w-prefix 定义移出语法引号,然后使用syntax-escape 来调用它。否则,你会得到更多的错误,因为defn 需要一个符号,所以一旦扩展宏必须生成一个defn 表单,其中第二项是符号。大致如下:

(defmacro create-dispatcher [name]
  (let [name-w-prefix #(symbol (str % "-" name))]
    `(do
       (def ~(symbol name) (atom ))
       (defn ~(name-w-prefix "get")
         ([] (println "no tag provided"))
         ([tag#] (println "called with tag" tag#))))))

请注意,根据我上面所说的内容,我已将 defn 正文中的 ~'[tag] 更改为 [tag#]

【讨论】:

感谢您的解释!不知道..但我做到了,现在它抱怨函数定义中的x符号.. 查看编辑。我认为您对语法引号内部和外部的内容有点困惑。 是的,看起来像!例如,我认为既然这都是关于 AST 的,我可以像 ~(expr) 从函数中返回,它会替换调用者,就好像在这里显式写的一样。但看起来~ 的工作方式不同......也感谢~' -> # 的建议!现在我已经修复了所有的宏,它工作正常!

以上是关于Clojure - 宏中的 let 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为什么ns非全局范围内的别名(let,def)不起作用?

LibreOffice Calc Goal Seek 在基本宏中不起作用

x 和 x 的集合在 Scheme 中的 let 中不起作用

在绑定向量中注释 Clojure

如果比较列表,Clojure的deftest宏中的断言错误

for let 循环不起作用?