let local approach:
foo compiles only once (when the top level form). The result of this compilation is a class that implements the clojure.lang.IFn interface; the actual body lives in the invoke(Object) method of this class. At runtime, each time control reaches a point in bar , where local foo is entered, a new instance of this class is provided; two foo calls use this instance of this class.
Here is an easy way to prove the "single compilation" property in REPL:
(defn bar [xy] (let [foo (fn [x] (* xx))] foo)) (identical? (class (bar 1 2)) (class (bar 1 2))) ;= true
NB. Clojure is smart enough to notice that foo not an “actual closure” (it closes the bar parameters, but doesn’t actually use them), so the foo runtime view doesn’t work transferring any additional fields that would close, but a new instance of the class foo nonetheless stands out with every bar call.
Separate defn approach:
There is one instance of foo , however, calling it involves an indirect pass through Var, which in itself has a non-zero value. This cost, as a rule, is not worth worrying about everything except the most performance-sensitive code, but it is there, so factoring a local function may not necessarily be a victory in performance. As usual, if it's worth it to optimize, you should first evaluate / compare.
let over lambda
There is also the final version mentioned by Daniel, where let passes, not inside, defn . With this approach, there is one instance (class) of foo ; it is stored in a field inside bar ; and it is used for all foo calls inside bar .
source share