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更骇人。无论如何,非常感谢你。