Dynamic Feature Search in Clojure Namespaces

I have several namespaces, each of which contains a function with the same name, for example:

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

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

(You can imagine resourceX, resourceY, resourceZetc. The actual function createwill eventually send HTTP POST and return an answer, but here it does not matter.)

Now, in a different namespace, I would like to define a function that takes two arguments: an array of resource names (i.e. the namespace name) and a function name, for example:

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

Therefore, I can write:

(do-verb :create :resourceX)

with the same effect as:

(myns.resourceX/create)

I tried using ns-resolvefor example:

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

But I'm not sure about the use ns-resolve- it seems to be a hack.

Another possibility I learned is defining a map for mapping characters to functions:

(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 , .

?

+4
2

, 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]))

do-verb verb :

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

:

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

, .

+4

,

: () , , , , , , , gotchas:

, "". .

; 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"
+1

All Articles