Clojure - 宏中的一个让步不起作用

时间:2013-02-27 20:32:13

标签: macros clojure let

我创建了一个宏,用于创建一个名为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!")
    ))

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

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

1 个答案:

答案 0 :(得分:8)

宏中的所有符号都在当前命名空间中解析,并且期望计算为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#))))))

请注意,我已根据上述内容将~'[tag]更改为[tag#]内的defn