Oprócz aspektu efektywności opisanej powyżej, jest aspekt bezpieczeństwa, który jest również przydatna.Rozważmy następujący kod:
(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.
Tak, za pomocą ^: const metadane przy definiowaniu Vars Vars te są skutecznie „inline” w każdym miejscu są używane. Wszelkie późniejsze zmiany w var nie mają zatem wpływu na żaden kod, w którym "stara" wartość została już wstawiona.
Użycie funkcji ^: const służy również do funkcji dokumentacji. Kiedy czyta się (def ^: const pi 3.14159) mówi czytelnikowi, że var pi nie ma nigdy zamiaru zmieniać, że jest po prostu wygodną (& mam nadzieję opisową) nazwą dla wartości 3.14159.
Powiedziawszy powyżej, zauważ, że nigdy nie używam ^:const
w moim kodzie, ponieważ jest zwodniczy i zapewnia "fałszywą pewność", że var nigdy się nie zmieni. Problem polega na tym, że ^:const
sugeruje, że nie można przedefiniować var, ale jak widzieliśmy z const-two
, to nie zapobiega zmianie var. Zamiast tego, ^:const
ukrywa fakt, że zmienna ma nową wartość, ponieważ const-two
została skopiowana/wstawiona (podczas kompilacji) do każdego miejsca użycia przed zmianą var (w czasie wykonywania).
Znacznie lepszym rozwiązaniem byłoby wyrzucenie wyjątku przy próbie zmiany ^:const
var.
Czy to oznacza, że następujące (def ^: const klucz-na-num {: jeden 1: dwa 2}) (def sum (+ (: jeden klawisz-na-num) (: dwa klucz do-do- num)) jest kompilowany do (def Podsumowując (+ 1 2)) –
nie, ^:? const działa tylko na wartościach pierwotnych, a nie dowolnych obiektów – noisesmith