2017-02-21 11 views
2

私はいくつかの異なるコンテキストで呼び出すことのできる関数を1つ持っているアプリケーションを持っています。自身は「文脈」を気にしないでください他の関数の内部に埋め込まれバーのようなさまざまな機能の束とはいえ関数の正しいバージョンを暗黙的に呼び出す

(defn foo [a context] 
    (-> a 
     inc 
     (#(bar % context)))) 

(defn bar [a context] 
    (cond (= context 1) (* a 2) 
     (= context 2) (/ a 2))) 

:だから、一例として、私はように機能コードを持っているかもしれません。

彼らは他の機能の中に埋め込まれているので(これらのコードを1つのコンテキストで作成して他のものを追加しているため)、これらの関数をすべて修正して、関連する '面倒です。私は解決策としてそれを本当に好きではありません。理想的には、私は暗黙のうちにそれぞれの文脈でbar関数の適切なバージョンを使用したいと思うでしょう。

プロトコルで問題が解決する場合があります。しかし、私の関数では、(実際の関数では)いくつかの 'bar'関数はコンテキストを使用し、それを使用する他の関数に渡すため、プロトコルを使用して関数を書き直すのは面倒です。だから、私はコードの一部を複製しなければならないと思います(あるいは、異なるプロトコルにフラグを渡す必要があります)。

私が思い描いた解決策は、fooのネームスペースを作成し、各コンテキストの別々のネームスペースを作成することでした。各コンテキストの名前空間では、別の名前空間を定義します。そしてfooを呼び出しネームスペースにあるbar関数のバージョンを呼び出すように変更します。つまり、context-1名前空間からfooを呼び出すと、意図したとおりに動作します。 context-2名前空間にも同じです。

異なる名前空間からコンテキスト1 fooとコンテキスト2 fooを呼び出す必要があることを除いて、これは基本的に機能します。それぞれの名前空間のラッパーを記述する必要があります。 。FOO関数呼び出しはそのように、コンテキスト-1ナノ秒で、私のような何かを書く前に私がで始めたネームスペースに切り替える:この作品

(defn context-1-foo [a] 
    (let [base-ns *ns*] 
    (in-ns 'context-1) 
    (let [result (foo a)] 
     (in-ns (ns-name base-ns)) 
     result))) 

、それは変更の全体の多くを必要としませんでしたが、私はそれが非慣用的でなければならないと思う。それは奇妙なバグを持っているような誘惑かもしれないようです。

これを行うにはどのような慣習的な方法がありますか?同様に、コードをほとんど変更する必要がない方法がありますか?

答えて

1

更新:

謝罪、私は、これはあなたが暗黙のすべきコンテキストのための必要条件だ解決できないことを見、質問を再読み込みした後。このシナリオを処理するための慣用的な方法は、Clojureののmultimethodsを使用することです

オリジナル回答を

(with-context 1 
    (bar 4)) 
;; => 8 

(with-context 2 
    (bar 4)) 
;; => 2 

:あなたはおそらくDynamic Bindingに探して、より良い運を持っているでしょうし、あなたは次のように動作するマクロを作成することができます。呼び出されるメソッドがコンテキストの内容に依存している場合、マルチメソッドは特定のコンテキストにマッチするメソッドをディスパッチすることを可能にします。また、それが一致するコンテキストを指定するだけで、より多くのメソッドを追加することができます。ご例えば

; you could define the context object like: 
{:method :bar-1} 
; or 
{:method :bar-2} 

;; Create multimethod that accepts the 2 params 
;; and dispatches on :method in context 
(defmulti bar (fn [a context] (:method context))) 

;; Method that dispatches when context is {:method :bar-1} 
(defmethod bar :bar-1 [a context] 
(* a 2)) 

;; Method that dispatches when context is {:method :bar-2} 
(defmethod bar :bar-2 [a context] 
    (/ a 2)) 

;; Method that dispatches when context is {:method :bar-3} 
(defmethod bar :bar-3 [a context] 
    ; Some third implementation 
) 

次に、あなたは、単に上で見たようバーの任意の将来の実装が簡単なdefmethodを追加することができます右のコンテキストオブジェクト

(bar 4 {:method :bar-1}) 
;; => 8 

(bar 4 {:method :bar-2}) 
;; => 2 

(bar 4 {:method :bar-3}) 
;; => nil 

barを呼び出します。

+0

リンクをありがとう、私はマルチメソッドについて知らなかった。あなたが言ったように、それらは暗黙的ではないので、動的バインディングも見ていきます。私はむしろ文脈がどれほど深い意味で使われているのかというと暗黙のうちにそれをやりたいのですが、それを暗黙のうちに行うのはあまりにも厄介なのであれば、私はおそらくそれを私が知っているマルチメソッドで慣用的に書き直すでしょう。 – Nick

+0

私は複数のバー機能、bar-1、bar-2、bar-3、bar-4などを持っていて、それぞれがコンテキスト1と2で異なる動作をしたいとします。私が(context-1(big-fn-containing-bar入力)を使って)呼び出すときに、bar-1、bar-2、bar-3などをコンテキスト1のバージョンに再バインドするようにそれらの機能のうち?ここのアイデアを正しく理解していますか? – Nick

+0

私はもう少し読んで、自分でいくつかのものをテストしました。ありがとう、私はダイナミックバインドがまさに私が必要とするものだと思います。これは本当にクールです。 – Nick

関連する問題