12

bindingフォームが clojure で再バインド可能な動的スコープを許可することを理解しています。これまでのところ、それが使用されているのを見た唯一の用途は、その時点で好きなライターにリバウンドするprint場所などの I/O です。*out*

binding他の施設では真に機能しないところのパワーを真に活用する例を見たいと思います。個人的には、ユーザーが提供したオブジェクトをすべての関数に渡すのが本当に面倒な場合にのみ使用しました。基本的に、ヘルパー関数が使用するコンテキストを作成しようとしている状況です。(このケースと同様に、いつ Clojure で一時的に再バインドする特別な変数のイディオムを使用する必要がありますか? ) より具体的に言うと、ユーザーに依存して、*db*var を使用して、データベース関数が操作対象を認識できるようにします。これは、ユーザーがデータベース関数に対して多数のネストされた呼び出しを記述する必要がある場合に特に役立ちました。通常、自分で作業を簡単にするためにマクロを作成する必要がある場合は問題ありませんが、ユーザーにそうするように要求するのは良くないように思えます。そうは言っても、私はできるだけそうしないようにしています。

コピーしてコードに組み込むことができる「バインディング」の他の良いユースケースは何ですか?

4

3 に答える 3

8

私がバインディングを使用する理由は 2 つあります。

  1. 他のシンボルの定数または他の値をオーバーライドするテストの実行
  2. データベース接続やメッセージ ブローカー チャネルなどの「グローバル」リソースを使用する

テスト

私は、メッセージ交換を介してメッセージを送信することによって通信するいくつかのコンポーネントを備えた分散システムに取り組んでいます。これらの取引所にはグローバルな名前があり、私は次のように定義しました:

(ns const)
(def JOB-EXCHANGE    "my.job.xchg")
(def CRUNCH-EXCHANGE "my.crunch.xchg")
;; ... more constants

これらの定数は、メッセージを適切な場所に送信するために多くの場所で使用されます。コードをテストするために、テスト スイートの一部で実際のメッセージ交換を使用するコードを実行します。ただし、テストが実際のシステムに干渉することは望ましくありません。

これを解決するために、bindingこれらの定数をオーバーライドする呼び出しでテスト コードをラップします。

;; in my testing code:
(binding [const/CRUNCH-EXCHANGE (str const/CRUNCH-EXCHANGE (gensym "-TEST-"))
          const/CRUNCH-TASK-QUEUE (str const/CRUNCH-TASK-QUEUE (gensym "-TEST-"))]
  ;; tests here
)

このbinding関数内では、定数を使用する任意のコードを呼び出すことができ、オーバーライドされた値が使用されます。

グローバル リソースの使用

バインディングを使用するもう 1 つの方法は、特定のスコープ内のグローバルまたはシングルトン リソースの値を「修正」することです。以下は、私が作成した RabbitMQ ライブラリの例です。RabbitMQ の値をConnectionシンボルにバインドし*amqp-connection*て、コードで使用できるようにしています。

(with-connection (make-connection opts)
  ;; code that uses a RabbitMQ connection
)

の実装with-connectionは非常に簡単です。

(def ^{:dynamic true} *amqp-connection* nil)

(defmacro with-connection
  "Binds connection to a value you can retrieve
   with (current-connection) within body."
  [conn & body]
  `(binding [*amqp-connection* ~conn]
     ~@body))

私の RabbitMQ ライブラリ内のすべてのコードで接続を使用でき、*amqp-connection*それが有効な open であると想定できますConnection。または(current-connection)、RabbitMQ 呼び出しを でラップするのを忘れた場合に説明的な例外をスローする関数を使用しwith-connectionます。

(defn current-connection
  "If used within (with-connection conn ...),
   returns the currently bound connection."
  []
  (if (current-connection?)
    *amqp-connection*
    (throw (RuntimeException.
      "No current connection. Use (with-connection conn ...) to bind a connection."))))
于 2011-08-24T02:30:38.953 に答える
2

VimClojureバックエンドでは、同じJVMで複数のreplが実行されている場合があります。ただし、Vimとバックエンド間の接続は継続的ではないため、コマンドごとに新しいスレッドを取得する可能性があります。そのため、コマンド間で状態を簡単に保持することはできません。

VimClojureが行うことは次のとおりです。、、、などのbindingすべての興味深い変数を設定します。次に、コマンドを実行し、その後、変更された可能性のあるVarを一部のbookeepingインフラストラクチャに保存します。*warn-on-reflection**1*2binding

したがって、すべてのコマンドは「私はrepl 4711に属しています」と言うだけで、そのreplの状態が表示されます。repl0815の状態に影響を与えることなく。

于 2011-08-24T06:21:12.533 に答える
2

バインド関数は、テストコードで非常に役立ちます。これは、関数を var に格納することの大きな利点の 1 つです (Clojure はデフォルトでそうしています)。

私が書いた暗号化プログラムからの抜粋。

(defmacro with-fake-prng [ & exprs ]
  "replaces the prng with one that produces consisten results"
  `(binding [com.cryptovide.split/get-prng (fn [] (cycle [1 2 3]))]
     ~@exprs))

キージェネレータ関数をどのように単体テストしますか? それは予測できないはずです。どこでもスレッド(if testing ...)化するか、ある種のモック フレームワークを使用できます。または、乱数ジェネレーターを「動的にモック」するマクロを使用して、これをテストコードにのみ配置して、生産側を無頓着にすることができます。

(deftest test-key-gen 
   (with-fake-prng 
         ....))
于 2011-08-24T18:00:32.317 に答える