动态查找Clojure命名空间中的函数

时间:2016-02-25 16:01:30

标签: clojure

我有许多名称空间,每个名称空间都包含一个具有相同名称的函数,例如:

(ns myns.resourceX
  ...)
(defn create
  (println "resourceX/create"))

(ns test)
(myns.resourceX/create)
(myns.resourceY/create)

(您可以想象有resourceXresourceYresourceZ等。实际的create函数最终会发送HTTP POST并返回响应,但这不是这里很重要。)

现在,在另一个命名空间中,我想定义一个带有两个参数的函数:一个资源名称数组(即命名空间名称)和函数名称,例如:

(defn do-verb
   [verb res-type]
   (??))

所以我可以写:

(do-verb :create :resourceX)

效果如下:

(myns.resourceX/create)

我尝试过的一件事是使用ns-resolve,例如:

(defn do-verb [verb res-type & params] (apply (ns-resolve (symbol (clojure.string/join ["myns." (name res-type)])) (symbol (name verb))) params))

但我不确定使用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,这有不利之处。

有没有替代方法?

2 个答案:

答案 0 :(得分:4)

如果您真的想在命名空间中进行动态查找,可以使用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)))

然而,这仍然看起来有点hacky,与使用ns-resolve相同。

因此,我建议您将do-verb函数设为多方法。

(defmulti do-verb (fn [verb res-type & args] [verb res-type]))

之后,为您的每个do-verb和资源类型实施verb

(defmethod do-verb [:create :resourceX] [_ _ & args] ...)

并按如下方式调用:

(do-verb :create :resourceX ...)

正如您所见,签名完全匹配。

答案 1 :(得分:1)

宏应该能够做到这一点,

但要非常小心以下代码 - 我以前从未写过(明智的)宏,所以虽然这似乎有用,但它没有经过测试,而且可能会有问题:

另请注意"用户。"是硬编码的。

sapply(dat, FUN = function(x) {x[substr(x, 1, 1) != substr(x, 3, 3)] <- NA; x})