質問に対する直接の答えではありませんが、使用している一般的なパターンがより一般的です。つまり、(データベースまたは別のサーバーへの)接続パラメーターを保持する単一のステートフルデータ構造を持つことです。ほとんどのフレームワークはこれを好転させます。接続パラメーターを保持している関数内から関数を呼び出す代わりに、接続データ構造をパラメーターとして受け入れる関数があります。
たとえば、データベース接続が与えられた場合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
データ構造があります。オブジェクト指向言語では、接続を保持するグローバルでステートフルなオブジェクトがあります(おそらくJavaランドではシングルトン)。Clojureでは、ライブラリは通常、with-...
マクロを使用してこれを処理します。マクロは、特定の接続を内部で使用される動的変数にバインドします。
(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
は、提供された接続、または接続が指定されていない場合はバインドされた接続を使用します。