Помимо аспекта эффективности, описанного выше, есть аспект безопасности, который также полезен. Рассмотрим следующий код:
(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.
Таким образом, используя метаданные ^: const при определении переменных, переменные эффективно "встраиваются" в каждое место, где они используются. Следовательно, любые последующие изменения в var не влияют ни на какой код, в котором «старое» значение уже встроено.
Использование ^: const также выполняет функцию документирования. Когда кто-то читает (def ^: const pi 3.14159), говорит читателю, что var pi никогда не предназначен для изменения, это просто удобное (и, надеюсь, описательное) имя для значения 3.14159.
Сказав выше, обратите внимание, что я никогда не использую ^:const
в своем коде, так как это обманчиво и дает "ложную гарантию", что переменная никогда не изменится. Проблема в том, что ^:const
подразумевает, что никто не может переопределить переменную, но, как мы видели с const-two
, это не препятствует изменению переменной. Вместо этого ^:const
скрывает тот факт, что переменная имеет новое значение, поскольку const-two
была скопирована / вставлена (во время компиляции) в каждое место использования до изменения переменной (во время выполнения).
Гораздо лучшим решением было бы создание исключения при попытке изменить ^:const
var.