2

ここで、「グラフ」は、スコープ内に構成が設定された関数を返す高階関数です。

(ns bulbs.neo4jserver.graph)

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

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

グラフのインスタンスを作成すると、それを使用して他の関数を呼び出し、config arg を自動的に渡すことができます。

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

(g out1)
;; => "out1"

これは機能します。ただし、グラフを別の名前空間にrequire/importする場合は、各関数呼び出しに接頭辞としてグラフの名前空間を付ける必要があります。

(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)))

これを行う最善の方法は何ですか?

4

2 に答える 2

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データ構造があります。オブジェクト指向言語では、接続を保持するグローバルでステートフルなオブジェクトがあります(おそらく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は、提供された接続、または接続が指定されていない場合はバインドされた接続を使用します。

于 2012-05-14T02:01:25.640 に答える
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 で説明されているように、通常の関数を使用します。

于 2012-05-13T14:41:15.050 に答える