上記の効率面に加えて、有用な安全面もある。次のコードを考えてみましょう:
(def two 2)
(defn times2 [x] (* two x))
(assert (= 4 (times2 2))) ; Expected result
(def two 3) ; Ooops! The value of the "constant" changed
(assert (= 6 (times2 2))) ; Used the new (incorrect) value
(def ^:const const-two 2)
(defn times2 [x] (* const-two x))
(assert (= 4 (times2 2))) ; Still works
(def const-two 3) ; No effect!
(assert (= 3 const-two )) ; It did change...
(assert (= 4 (times2 2))) ; ...but the function did not.
したがって、varsを定義するときに^:constメタデータを使用することによって、変数は使用されるすべての場所に効果的に "インライン"されます。したがって、その後のvarの変更は、「古い」値がすでにインライン化されているコードには影響しません。
The use of ^:const also serves a documentation function. When one reads (def ^:const pi 3.14159) is tells the reader that the var pi is not ever intended to change, that it is simply a convenient (& hopefully descriptive) name for the value 3.14159.
前述のように、私のコードでは決して ^:const
を使用しないことに注意してください。なぜなら、それは不正であり、varが決して変更されないという誤った保証を提供するからです。問題は ^:const
はvarを再定義できないことを意味しますが、 const-two
を見てもvarが変更されることはありません。代わりに、 const-two
はコンパイル時に各使用場所にコピー/インライン化されているので、 ^:const
はvarに新しい値があることを隠します。 varが変更される前に(実行時に)
もっと良い解決策は、 ^:const
varを変更しようとすると例外をスローすることです。