Clojure命名空间中函数的动态查找
我有许多名称空间,每个名称空间包含一个同名函数,例如:Clojure命名空间中函数的动态查找,clojure,Clojure,我有许多名称空间,每个名称空间包含一个同名函数,例如: (ns myns.resourceX ...) (defn create (println "resourceX/create")) (ns test) (myns.resourceX/create) (myns.resourceY/create) (defn do-verb [verb res-type] (??)) (您可以想象拥有resourceX、resourceY、resourceZ等等。实际的creat
(ns myns.resourceX
...)
(defn create
(println "resourceX/create"))
(ns test)
(myns.resourceX/create)
(myns.resourceY/create)
(defn do-verb
[verb res-type]
(??))
(您可以想象拥有resourceX
、resourceY
、resourceZ
等等。实际的create
函数最终发送HTTP帖子并返回响应,但这在这里并不重要。)
现在,在另一个名称空间中,我想定义一个具有两个参数的函数:资源名称数组(即名称空间名称)和函数名称,例如:
(ns myns.resourceX
...)
(defn create
(println "resourceX/create"))
(ns test)
(myns.resourceX/create)
(myns.resourceY/create)
(defn do-verb
[verb res-type]
(??))
所以我可以写:
(do-verb :create :resourceX)
大意与:
(myns.resourceX/create)
我尝试过的一件事是使用ns resolve
,例如:
(defn do动词)
[动词res类型和参数]
(应用(ns)
(符号(clojure.string/join[“myns.”(name-res-type)])
(符号(名称动词)))
参数)
但我不确定是否使用ns resolve
——这似乎是一种黑客行为
我探讨的另一种可能性是定义一个映射,将符号与函数关联起来:
(def convert-fns
{:resourceX {:create resourceX/create}
:resourceY {:create resourceY/create}
...})
(defn do-verb [verb res-type & params]
(apply (get-in convert-fns [res-type verb]) params))
但对我来说,这有一个缺点,即每次添加新资源时都需要修改convert fns
有什么替代方法吗?宏应该能够做到这一点 但是要非常小心下面的代码-我以前从未编写过(合理的)宏,因此,虽然这似乎可行,但它没有经过测试,可能会出现问题: 还要注意,“用户”是硬编码的
; in user ns
user=> (defmacro do-verb [n r]
(list (symbol (str "user." (name r) "/" (name n)))))
; quick test
user=> (macroexpand '(do-verb :x :b))
; (user.b/x)
; create a fn in user.b
user.b=> (defn x [] "yay")
; execute new macro
user=> (do-verb :x :b) ; calls (user.b/x)
"yay"
如果确实要在名称空间中执行动态查找,可以使用
ns publics
函数
请参见以下代码段:
(defn do-verb [verb res-type & params]
(let [ns-symbol (symbol (str "myns." (name res-type)))
publics (ns-publics ns-symbol)
;; Looking up function we need
func (publics (symbol (name verb)))]
(apply func params)))
然而,这似乎仍然有点不太成熟,就像使用ns resolve
一样
因此,我建议您将do-verb
函数设置为多方法
(defmulti do-verb (fn [verb res-type & args] [verb res-type]))
之后,为您的每个verb
和资源类型实施do verb
:
(defmethod do-verb [:create :resourceX] [_ _ & args] ...)
并称之为:
(do-verb :create :resourceX ...)
正如你所看到的,签名是完美匹配的。这是一个很好的方法,我喜欢的一点是,你可以将
(defmethod do verb:default[\uuuu&args]…)
定义为一个“catch undefined”子句。你说,ns publics
听起来像ns resolve
一样“hacky”defmethod
,虽然是一种有趣的方法,但需要为每种新的资源类型添加新的专业化。。。不过还是要谢谢你@sergio我相信“ns publics
”方法是我们能得到的最简单的方法,因为它正是这个问题的标题所写的:在命名空间中动态查找函数。很可能就是这样。。。我会再等一会儿再接受答复。您能详细说明一下您看到的ns resolve
和ns publics
之间的区别吗?谢谢。这是有效的——即使它do动词
被定义为一个函数而不是一个宏,它也是有效的。说实话,这感觉比使用ns resolve更骇人。无论如何,非常感谢你。