Search code examples
clojure

Correct def for expression


I have this function:

(defn get-validator []
  (UrlValidator. (into-array ["https"])))

I want it to be evaluated only once, on the first call, then just return the result. Which is the better way to write it:

(def get-validator (UrlValidator. (into-array ["https"])))

(def ^:const get-validator (UrlValidator. (into-array ["https"])))

(defonce get-validator (UrlValidator. (into-array ["https"])))

Or is there another way that is better? Documentation suggests that defonce is the correct one, but it's not clearly stated.


Solution

  • First of all, def sets a var with the given name and optionally the given "init" value in that var, when the namespace is loaded/compiled (note that there is basically not much of a difference between loading and compiling and that's the reason, why you don't want to have side-effects in your def).

    Roughly all three versions are the same, with the following differences:

    • :^const allows inlining this value; so this effects the following forms, when they are compiled and might improve performance in the following code
    • defonce prevents re-def-ining the var again once the namespace is reloaded; this is most useful if you def mutable state, that you want to survive over reloading your code

    In any case, requiring the ns for the first time, will execute the code to init the var. Then it is basically left alone (imagine a static property in a Java class with a static initializer).

    All that said: if UrlValidator has internal state you might still be better off using a function to create a fresh one, whenever you need it.