在Clojure中对函数使用“apply”时,如何显式指定命名空间?

时间:2012-05-13 14:14:06

标签: clojure

这里“graph”是高阶函数,它返回一个在其范围内配置了set的函数:

(ns bulbs.neo4jserver.graph)

(defn out1
  "Test func that simply returns out1."
  [config]
  "out1")

(defn graph
  [config]
  (fn [func & args]
    (apply func config args)))

您创建了一个图形实例,然后可以用它来调用其他函数并自动传入配置参数:

(def g (graph {:root-uri "http://localhost"}))

(g out1)
;; => "out1"

这有效;但是,如果您需要/将图形导入另一个命名空间,则必须使用图形命名空间为每个函数调用添加前缀:

(ns bulbs.neo4jserver.junk
  (:require [bulbs.neo4jserver.graph :as graph]))

(def g (graph/graph {:root-uri "http://localhost"}))

;; would rather do (g out1)
(g graph/out1)

相反,我想在apply函数中明确指定命名空间,以便用户不必:

(defn graph
  [config]
  (fn [func & args]
    ;; somehow specify the graph namespace here
    (apply func config args)))

最好的方法是什么?

2 个答案:

答案 0 :(得分:3)

不是您问题的直接答案,但您使用的一般模式更常见:拥有一个包含连接参数的单一状态数据结构(到数据库或其他服务器)。大多数框架都会改变这种情况:它不是像保存连接参数那样从函数中调用函数,而是具有接受连接数据结构作为参数的函数。

例如,给定数据库连接conn,典型的虚构数据库库可能如下所示(注意:为简化起见,示例已经简化):

(let [conn (make-db-connection :host .... :user ....)]
  (read-from-db conn :user))

使用库作为消息传递框架(例如,RabbitMQ)可能如下所示:

(let [conn (make-amqp-connection :host .... :port ...)]
  (send-message conn :my-queue "hello world"))

在这两种情况下,都有一个conn数据结构,用于对库函数的所有后续调用。在OO语言中,您将拥有一个持有连接的全局有状态对象(可能是在Java领域中的单例)。在Clojure中,库通常使用with-...宏处理此问题,该宏将特定连接绑定到内部使用的动态var:

(with-db-connection (make-db-connection ....)
  (read-from-db :user ....))

(with-rabbit-connection (make-rabbitmq-connection ....)
  (send-message :my-queue "hello world"))

这是实现此模式的(虚构)示例。假设连接是Java对象:

;; a var to hold the connection
(def ^:dynamic *current-connection* nil)


(defmacro with-connection [conn & body]
  `(binding [*current-connection* ~conn]
     ~@body))

;; send-msg is using the connection object bound to
;; the *current-connetion* var
(defn send-msg [msg]
  (.sendMessage *current-connection* msg))

;; usage:
(with-connection conn
  (send-msg "hello world!"))

如果您想要花哨,可以通过定义send-msg函数来支持这两种模式(使用绑定连接接受连接作为参数):

(defn send-msg [msg & {:keys [connection]
                       :or {connection *current-connection*}}]
  (.sendMessage connection msg))

;; usage with bound connetion:
(with-connection conn
  (send-msg "Hello World!"))

;; usage with explicit connection:
(send-msg "Hello World!"
          :connection conn)

如果未指定连接,此版本的send-msg将使用提供的连接绑定连接。

答案 1 :(得分:2)

您可以传递符号而不是函数,并在graph函数中解析它:

(defn graph
  [config]
  (fn [func & args]
    (apply (ns-resolve 'bulbs.neo4jserver.graph func) config args)))

并称之为:

(ns bulbs.neo4jserver.junk
  (:require [bulbs.neo4jserver.graph :as graph]))

(def g (graph/graph {:root-uri "http://localhost"}))

(g 'out1)

g不再是高阶函数。它需要符号,而不是功能。我个人不喜欢这种方法。你为什么不喜欢指定命名空间?也许你可以用宏做你需要的东西,但我不太了解宏。

修改

不要这样做。在评论中使用@Ankur和@Gert解释的常规函数​​。