Macros 多次使用Clojure宏生成函数

Macros 多次使用Clojure宏生成函数,macros,clojure,Macros,Clojure,我在这里有点进退两难,我无法理解。我正在尝试创建一系列函数,除了一些东西(包括它们所使用的参数数量)之外,它们都非常相似 我编写了一个宏来创建其中一个似乎工作正常的函数。这是宏 (defmacro gen-fn [meth args transform-fns] `(fn [~'conn ~@args] (->> (. metadata-service ~meth (sec/single-use-ticket ~'co

我在这里有点进退两难,我无法理解。我正在尝试创建一系列函数,除了一些东西(包括它们所使用的参数数量)之外,它们都非常相似

我编写了一个宏来创建其中一个似乎工作正常的函数。这是宏

(defmacro gen-fn
  [meth args transform-fns]
  `(fn [~'conn ~@args]
     (->> (. metadata-service
            ~meth
            (sec/single-use-ticket ~'conn)
            ~@args)
          ~@transform-fns)))
所以我可以创建两个函数

(def get-source (gen-fn getSource [version source] [bean]))
(def get-all-sources (gen-fn getAllSources [version] [(map bean)]))
(doseq [[function-label [args transform-fns]] metadata-methods]
  (intern *ns* (symbol (->kebab-case function-label))
               (gen-fn function-label [version source] [bean])))
当我这样称呼它们时,它们都能正常工作:

(get-source conn "2013AB" "WHO97")
(get-all-sources conn "2013AB")
(def metadata-methods
  { "getSources" [["version" "source"] ["bean"]]
    "getAllSources" [["version"] ["(map bean)"]] })
现在我有大约600个这样的函数要创建,所以如果我能稍微简化一下就好了(最终可能会在应用程序启动时从外部源读取它)。所以我在这里的第一个想法是构建一张这样的地图:

(get-source conn "2013AB" "WHO97")
(get-all-sources conn "2013AB")
(def metadata-methods
  { "getSources" [["version" "source"] ["bean"]]
    "getAllSources" [["version"] ["(map bean)"]] })
或者类似的东西,然后使用
doseq
或类似的东西来创建函数

(def get-source (gen-fn getSource [version source] [bean]))
(def get-all-sources (gen-fn getAllSources [version] [(map bean)]))
(doseq [[function-label [args transform-fns]] metadata-methods]
  (intern *ns* (symbol (->kebab-case function-label))
               (gen-fn function-label [version source] [bean])))
当我运行这个函数时,它似乎可以工作,但是调用
(get source conn“2013AB”“WHO97”)
会抛出一个异常,表示类代理没有匹配的方法“function\u label”

因此,宏无法正确创建函数

所以我的问题是 1) 有没有一个简单的方法来实现这一点? 2) 我是不是把事情弄得比它需要的更复杂了?有没有更简单的方法来完成同样的事情


普通函数可以工作,但要生成的每个函数都有不同数量的参数,我真的希望每个函数都有一个固定的arity。

宏作为参数传递给宏调用中的实际参数表达式,因此在此调用中:

(gen-fn function-label [version source] [bean])
gen fn
将传递实际符号
函数标签
,两个符号的向量和一个符号的向量作为参数。这就是为什么
get source
不能与
doseq
方法一起工作的原因

完成这类工作的通常方法是定义一个宏,如
gen fn
和另一个宏,比如
def fns
(按照通常的模式,使用原始宏名称的复数版本,并将
gen
更改为
def
,因为我们正在创建Vars),要发出多个封装在
do
中的
gen fn
表单,请执行以下操作:

(defmacro def-fns [& args]
  `(do ~@(for [[meth args transform-fns] (partition 3 args)
               :let [name (symbol (->kebab-case (str meth)))]]
           `(def ~name (gen-fn ~meth ~args ~transform-fns)))))
然后说

(def-fns
  getSource [version source] [bean]
  getAllSources [version] [(map bean)])
如果您更愿意使用地图,也可以:

;; def the map first
(defmacro def-fns []
  `(do ~@(for [[meth [args transform-fn]] metadata-methods
               :let [name (symbol (->kebab-case meth))]
           `(def ~name (gen-fn ~meth ~args ~transform-fn)))))

请注意,宏函数将使用
元数据方法的编译时值(这很好)。

这可能会帮助您:这太棒了,Michal!我很感激。在这个过程中你也教了我很多。