2016-08-09 5 views
0

特定のvarに依存するClojureコードを実行したいですが、が定義されている場合にのみが実行されます。 symが定義されている場合簡略化した例として、if形の本体にのみ実行されるべきである:シンボルが定義されている場合にのみ使用する方法

(if (resolve 'sym) (println sym)) 

残念ながら、これは動作しません。 symが定義されていない場合、コンパイラはまだそれを解決しようとスロー:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: sym in this context 

リッチHickleyさんのコメントhereを読んでから、私はこの動作は(主に)シングルパスのコンパイルのClojureのの使用によるものであることを集まりました。しかし、それは意味をなさず、明らかにこの場合望ましくない挙動を引き起こす。

私は、実行時に発生するシンボル解決を強制することで問題を回避することができます

(if (resolve 'sym) (println (deref (resolve 'sym)))) 

しかし、これは望ましくないハックです。 Clojureの読み込みモデルでは、より良い方法があるのでしょうか?

(なぜ私は私のprofiles.cljで定義された複数の構成可能なプロファイルがありますか?これを実行する必要があります。そのうちの一つは、私は便利なアクセス.名前空間に様々な機能を注入することを可能にする、vinyasaためである。その他は、様々な他のユーティリティを読み込みますヴィンヤサがロードされている場合にのみ、ライブラリ。それらの他のユーティリティライブラリのプロファイルでは、私が最も重要な機能を注入するヴィンヤサを使用したい、けど。私は上記のハックのcurrently using変化しています。)

答えて

1

アプローチは、 @Michielによってwhen-letで捧げられてこの問題を解決する最善の方法です。

(when-let [foo (resolve 'foo)] 
    (foo)) 
;;=> nil 

(defn foo [] 
    :bar) 

(foo) 
;;=> :bar 

(when-let [foo (resolve 'foo)] 
    (foo)) 
;;=> :bar 
+1

これは非常に面白いです!ここでは何が起きているのかはすぐに分かりませんが、Varを関数として呼び出すと、関数自体を逆関数にして、それが指す関数を呼び出しているようです。特に、これは、関数ではないVarsに対してこのトリックを使用したい場合は、それらを手作業で逆参照する必要があるが、関数であるVarsのためにする必要はないことを意味する。 –

+1

@RadonRosboroughうん、まさにそうだ。 [Varソースコード](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java#L374-L376)をチェックすることで確認できます。 –

1
(defn get-when-var-defined [sym] 
    (when-let [var-for-sym (resolve sym)] 
    @var-for-sym)) 

(get-when-var-defined 'foo) ;;=> nil 
(def foo 1) 
(get-when-var-defined 'foo) ;;=> 1 
+0

うーん...ので、基本的に、私が持っているものと同じですが、機能に抽象化:重要なことは、既存のバインディングをシャドウするletの能力を使って、コンディショナリティーがほぼ完全に透明にすることができますか?私はそれが十分妥当だと思う。代替ソリューションが提示されていない場合、私はこれを現在受け入れます。 –

+1

あなたは当然の機能を使う必要はありません。 '(when-let [v(解決 'sym)] @v])'はこれの最も基本的なバージョンです –

関連する問題