Besides the efficiency aspect described above, there is a safety aspect that is also useful. Consider the following code:
(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.
So, by using the ^:const metadata when defining vars, the vars are effectively "inlined" into every place they are used. Any subsequent changes to the var, therefore, do not affect any code where the "old" value has already been inlined.
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.
Having said the above, note that I never use ^:const
in my code, since it is deceptive and provides "false assurance" that a var will never change. The problem is that ^:const
implies one cannot redefine a var, but as we saw with const-two
it does not prevent the var from being changed. Instead, ^:const
hides the fact that the var has a new value, since const-two
has been copied/inlined (at compile-time) to each place of use before the var is changed (at run-time).
A much better solution would be to throw an Exception upon attempting to change a ^:const
var.