Clojure

Varとグローバル環境

Clojureは、変化する値への永続的な参照を維持する必要がある場合を認識した実用的な言語であり、制御された方法でそれを行うための4つの異なるメカニズム(Var、Refエージェントアトム)を提供します。 Varは、スレッドごとに動的に再バインド(新しいストレージロケーションに)できる可変ストレージロケーションを参照するためのメカニズムを提供します。すべてのVarは、ルートバインディングを持つことができます(必須ではありません)。ルートバインディングは、スレッドごとのバインディングを持たないすべての スレッドで共有されるバインディングです。したがって、Varの値は、そのスレッドごとのバインディングの値、または、値を要求しているスレッドにバインドされていない場合は、ルートバインディングの値(存在する場合)です。

特殊形式defは、Varを作成(およびインターン)します。 Varがまだ存在せず、初期値が指定されていない場合、varは未バインドです。

user=> (def x)
#'user/x
user=> x
#object[clojure.lang.Var$Unbound 0x14008db3 "Unbound: #'user/x"]

初期値を指定すると、ルートがバインドされます(既にバインドされている場合でも)。

user=> (def x 1)
#'user/x

user=> x
1

デフォルトではVarは静的ですが、マクロbindingを介してスレッドごとのバインディングを許可するために、Varを動的としてマークできます。 各スレッド内では、スタック規律に従います。

user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2

user=> (binding [x 2 y 3]
         (+ x y))
5

user=> (+ x y)
2

bindingで作成されたバインディングは、他のスレッドからは見えません。 同様に、bindingで作成されたバインディングは割り当てることができ、ネストされたコンテキストがコールスタックに配置される前にコードと通信する手段を提供します。 この機能は、上記のコードブロックのように、メタデータタグ^:dynamicをtrueに設定することによってのみオプトインできます。 コンテキスト内で静的Varを再定義したい場合があります。Clojure(バージョン1.3以降)は、そのような目的のために関数with-redefswith-redefs-fnを提供しています。

defnで定義された関数はVarに格納されるため、実行中のプログラムで関数を再定義できます。 これにより、アスペクト指向プログラミングまたはコンテキスト指向プログラミングの可能性の多くも実現します。 たとえば、特定のコールコンテキストまたはスレッドでのみ、ロギング動作で関数をラップできます。

バインディングの伝達

一部のClojure並行処理関数(futures、エージェント)は、「バインディング伝達」を提供します。これにより、現在の動的バインディングのセットを別のスレッドに伝達して、同じ環境で非同期的に作業を続行できます。 この機能は、futuresendsend-off、およびpmapによって提供されます。

(def ^:dynamic *num* 1)
(binding [*num* 2] (future (println *num*)))
;; prints "2", not "1"

(set! var-symbol expr)

代入の特殊形式。

最初のオペランドがシンボルの場合、グローバルvarに解決される必要があります。 varの現在のスレッドバインディングの値は、exprの値に設定されます。 現在、set!を使用してvarのルートバインディングを設定しようとするとエラーが発生します。つまり、varの割り当てはスレッドローカルです。 いずれの場合も、exprの値が返されます。

注-関数のパラメーターまたはローカルバインディングに割り当てることはできません。 Clojureでは、Javaフィールド、Var、Ref、およびエージェントのみが変更可能です

Javaフィールドへのset!の使用については、Java相互運用で説明されています。

インターン

名前空間システムは、シンボルからVarオブジェクトへのグローバルマップを保持します名前空間を参照)def式が、定義されているシンボルの現在の名前空間にインターンされたエントリを見つけられない場合、エントリを作成します。それ以外の場合は、既存のVarを使用します。 この検索または作成プロセスは、インターンと呼ばれます。 これは、マップ解除されていない限り、Varオブジェクトは安定した参照であり、毎回ルックアップする必要がないことを意味します。 また、名前空間はグローバル環境を構成し、評価で説明されているように、コンパイラはすべての自由シンボルをVarとして解決しようとします。

var特殊形式または#'リーダーマクロリーダーを参照)を使用して、現在の値ではなく、インターンされたVarオブジェクトを取得できます。

インターンされていないVar

with-local-varsを使用することにより、インターンされていないvarを作成できます。 これらのvarは、自由シンボルの解決中には見つからず、それらの値には手動でアクセスする必要があります。 しかし、それらは便利なスレッドローカルの可変セルとして機能することができます。

Varメタデータ

varを作成するフォームdefdefndefmacroなどは、標準のvar メタデータセットを使用してvarを記述します。 これらのフォームの一部は、明示的な構文を使用してメタデータに格納されている値を受け入れますが、一般に、そのメタデータをvarシンボル上のマップとして指定することもできます。

一般的なvarメタデータキー(すべてvar定義ではオプション)

  • :doc - varを文書化する文字列。通常、docstringパラメータによって設定されます

  • :added - このvarが追加されたバージョンを文書化する文字列

  • :private - 多くの場合、defn-によって設定されるブール値フラグ。作成者によって、このvarが実装の詳細であるという意図を表明するために使用されます。 プライベートvarはグローバルにアクセスできますが、プライベートではないvarにフィルタリングするns-…​関数では参照またはリストされません。

  • :arglists - arglistsのコレクション。指定されていない場合は自動的に生成されます。ほとんどの場合、マクロ構文を文書化するために使用されます

  • :macro - defmacroによって自動的に追加されるブール値フラグ(通常は直接使用されません)

  • :tag - varの値の型、またはvarに保持されている関数の戻り値の型の型識別子(通常はクラス)。 varメタデータは評価されるため、varの^longなどの型ヒントは、longプリミティブ型ヒントではなく、long関数に評価されます。 一般に、defn varのarglistに型ヒントを使用することをお勧めします。

  • :test - clojure.testフレームワークは、このキーを使用してユニットテストをvarに添付します(通常は直接使用されません)

  • :dynamic - varがスレッドコンテキストで動的に再バインドされる可能性があることを示します(上記を参照)。 動的varは、直接リンクでコンパイルする場合、直接リンクされません。

  • :redef - 直接リンクでコンパイルする場合、varを直接リンクしないことを示します(再定義できるようにするため)

  • :static - 使用されなくなりました(元々varはデフォルトで動的でしたが、現在はデフォルトで静的です)

  • :const - varがコンパイル時定数であり、コンパイラが値をそれを使用するコードにインラインできることを示します。 注:これはめったに必要ではなく、コンパイル時(読み取りは行いますが、評価は行いません)の定数(数値、文字列など(クラス、関数、参照型などではありません))でのみ機能します。 const varを再定義または動的にバインドしても、既にコンパイルされてランタイムにロードされているvarを使用するコードには影響しません。

コンパイル中の直接リンクとメタデータの省略の詳細については、コンパイラオプションも参照してください。

defのバリアント:defn defn- definline defmacro defmethod defmulti defonce defstruct
インターンされたVarの操作:declare intern binding find-var var
Varオブジェクトの操作:with-local-vars var-get var-set alter-var-root var? with-redefs with-redefs-fn
Varバリデーター:set-validator! get-validator
Varメタデータの使用:doc find-doc test