我有点困惑,我无法弄明白。我正在尝试创建一些非常相似的函数,除了几个东西(包括它们所采用的参数的数量)。
我编写了一个宏来创建其中一个似乎正常工作的函数。这是宏
(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)]))
当我这样称呼它们时两者都正常工作:
(get-source conn "2013AB" "WHO97")
(get-all-sources conn "2013AB")
现在我有大约600个这样的函数要创建,所以如果我可以简化这一点(最终可能在应用程序启动时从外部源读取它)会很好。所以我在这里首先想到的是构建一个这样的地图:
(def metadata-methods
{ "getSources" [["version" "source"] ["bean"]]
"getAllSources" [["version"] ["(map bean)"]] })
或沿着这些行的某些东西然后使用doseq
或类似的东西来创建函数
(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_label”...
所以不知何故宏没有正确创建函数。
所以我的问题是 1)有一种简单的方法可以使这项工作? 2)我制作的东西比它需要的更复杂吗?是否有更简单的方法来完成同样的事情?
普通函数可以工作,除了要生成的每个函数都使用不同数量的参数这一事实,我真的希望每个函数都有一个固定的arity。
答案 0 :(得分:3)
宏作为参数传递给宏调用中的实际参数表达式,因此在此调用中:
(gen-fn function-label [version source] [bean])
gen-fn
将传递实际符号function-label
,两个符号的向量和一个符号的向量作为参数。因此,get-source
无法使用doseq
方法。
完成此类操作的常用方法是定义一个宏,如gen-fn
和另一个宏,例如def-fns
(遵循使用原始宏名称的复数版本的通常模式,将gen
更改为def
,因为我们正在创建Vars),以便发出包含在gen-fn
中的多个do
表单:
(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)))))
请注意,宏函数将使用metadata-methods
的编译时值(这很好)。