When to use a Var instead of a function?

Hopefully this small example will get you on track:

> (defn your-handler [x] x)
#'your-handler

> (defn wrap-inc [f]
    (fn [x]
      (inc (f x))))
> #'wrap-inc

> (def your-app-with-var (wrap-inc #'your-handler))
#'your-app-with-var

> (def your-app-without-var (wrap-inc your-handler))
#'your-app-without-var

> (your-app-with-var 1)
2

> (your-app-without-var 1)
2

> (defn your-handler [x] 10)
#'your-handler

> (your-app-with-var 1)
11

> (your-app-without-var 1)
2

The intuition for this is when you use a var when creating your handler you are actually passing a "container" with some value, content of which can be changed in future by defining var with the same name. When you don't use var (like in your-app-without-var) you are passing a current value of this "container", which cannot be redefined in any way.


Yes, a Var in Clojure is similar to a C pointer. This is poorly documented.

Suppose you create a function fred as follows:

(defn fred [x] (+ x 1))

There are actually 3 things here. Firstly, fred is a symbol. There is a difference between a symbol fred (no quotes) and the keyword :fred (marked by the leading : char) and the string "fred" (marked by a double-quote at both ends). To Clojure, each of them is composed of 4 characters; i.e. neither the colon of the keyword nor the double-quotes of the string are included in their length or composition:

> (name 'fred)
"fred"
> (name :fred)
"fred"
> (name "fred")
"fred"

The only difference is how they are interpreted. A string is meant to represent user data of any sort. A keyword is meant to represent control information for the program, in a readable form (as opposed to "magic numbers" like 1=left, 2=right, we just use keywords :left and :right.

A symbol is meant to point to things, just like in Java or C. If we say

(let [x 1
      y (+ x 1) ]
  (println y))
;=> 2

then x points to the value 1, y points to the value 2, and we see the result printed.

the (def ...) form introduces an invisible third element, the Var. So if we say

(def wilma 3)

we now have 3 objects to consider. wilma is a symbol, which points to a Var, which in turn points to the value 3. When our program encounters the symbol wilma, it is evaluated to find the Var. Likewise, the Var is evaluated to yield the value 3. So it is like a 2-level indirection of pointers in C. Since both the symbol and the Var are "auto-evaluated", this happens automatically and invisibly and you don't have to think about the Var (indeed, most people aren't really aware the invisible middle step even exists).

For our function fred above, a similar situation exists, except the Var points to the anonymous function (fn [x] (+ x 1)) instead of the value 3 like with wilma.

We can "short-circuit" the auto-evaluation of the Var like:

> (var wilma)
#'clj.core/wilma

or

> #'wilma
#'clj.core/wilma

where the reader macro #' (pound-quote) is a shorthand way of calling the (var ...) special form. Keep in mind that a special form like var is a compiler built-in like if or def, and is not the same as a regular function. The var special form returns the Var object attached to the symbol wilma. The clojure REPL prints the Var object using the same shorthand, so both results look the same.

Once we have the Var object, auto-evaluation is disabled:

> (println (var wilma))
#'clj.core/wilma

If we want to get to the value that wilma points to, we need to use var-get:

> (var-get (var wilma))
3
> (var-get    #'wilma)
3

The same thing works for fred:

> (var-get #'fred)
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]
> (var-get (var fred))
#object[clj.core$fred 0x599adf07 "clj.core$fred@599adf07"]

where the #object[clj.core$fred ...] stuff is Clojure's way of representing a function object as a string.

With regard to the web server, it can tell via the var? function or otherwise if the supplied value is the handler function or the var which points to the handler function.

If you type something like:

(jetty/run-jetty handler)

the double auto-evaluation will yield the handler function object, which is passed to run-jetty. If, instead, you type:

(jetty/run-jetty (var handler))

then the Var which points to the handler function object will be passed to run-jetty. Then, run-jetty will have to use an if statement or equivalent to determine what it has received, and call (var-get ...) if it has received a Var instead of a function. Thus, each time through (var-get ...) will return the object to which the Var currently points. So, the Var acts like a global pointer in C, or a global "reference" variable in Java.

If you pass a function object to run-jetty, it saves a "local pointer" to the function object and there is no way for the outside world to change what the local pointer refers to.

You can find more details here:

  • http://clojure.org/reference/evaluation
  • http://clojure.org/reference/vars

Update

As OlegTheCat has pointed out, Clojure has yet another trick up its sleeve regarding Var objects that point to Clojure functions. Consider a simple function:

(defn add-3 [x] (+ x 3))
; `add-3` is a global symbol that points to
;     a Var object, that points to
;         a function object.

(dotest
  (let [add-3-fn  add-3           ; a local pointer to the fn object
        add-3-var (var add-3)]    ; a local pointer to the Var object
    (is= 42 (add-3 39))           ; double deref from global symbol to fn object
    (is= 42 (add-3-fn 39))        ; single deref from local  symbol to fn object
    (is= 42 (add-3-var 39)))      ; use the Var object as a function 
                                  ;   => SILENT deref to fn object

If we treat a Var object as a function, Clojure will SILENTLY deref it into the function object, then invoke that function object with the supplied args. So we see that all three of add-3, add-3-fn and add-3-var will work. This is what is occurring in Jetty. It never realizes that you have given it a Var object instead of a function, but Clojure magically patches up that mismatch without telling you.

Sidebar: Please note this only works since our "jetty" is actually the Clojure wrapper code ring.adapter.jetty, and not the actual Java webserver Jetty. If you tried to depend on this trick with an actual Java function instead of a Clojure wrapper, it would fail. Indeed, you must use a Clojure wrapper like proxy in order to pass a Clojure function to Java code.

You have no such guardian angel to save you if you use the Var object as anything other than a function:

  (let [wilma-long  wilma         ; a local pointer to the long object
        wilma-var   (var wilma)]  ; a local pointer to the Var object
    (is (int? wilma-long))        ; it is a Long integer object
    (is (var? wilma-var))         ; it is a Var object

    (is= 4 (inc wilma))          ; double deref from global symbol to Long object
    (is= 4 (inc wilma-long))     ; single deref from local  symbol to Long object
    (throws? (inc wilma-var))))  ; Var object used as arg => WILL NOT deref to Long object

So, if you are expecting a function and someone gives you a Var object that points to a function, you are OK since Clojure silently fixes the problem. If you are expecting anything other than a function and someone gives you a Var object that points to that thing, you are on your own.

Consider this helper function:

(defn unvar
  "When passed a clojure var-object, returns the referenced value (via deref/var-get);
  else returns arg unchanged. Idempotent to multiple calls."
  [value-or-var]
  (if (var? value-or-var)
    (deref value-or-var) ; or var-get
    value-or-var))

Now you can safely use the thing you were given:

(is= 42 (+ 39 (unvar wilma))
        (+ 39 (unvar wilma-long))
        (+ 39 (unvar wilma-var)))

Appendix

Notice that there are three dualities that can confuse the issue:

  • Both var-get and deref do the same thing with a Clojure Var
  • The reader macro #'xxx is translated into (var xxx)
  • The reader macro @xxx is translated into (deref xxx)

So we have (confusingly!) many ways of doing the same thing:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(def wilma 3)
; `wilma` is a global symbol that points to
;     a Var object, that points to
;         a java.lang.Long object of value `3`

(dotest
  (is= java.lang.Long (type wilma))

  (is= 3 (var-get (var wilma)))
  (is= 3 (var-get #'wilma))

  ; `deref` and `var-get` are interchangable
  (is= 3 (deref (var wilma)))
  (is= 3 (deref #'wilma))

  ; the reader macro `@xxx` is a shortcut that translates to `(deref xxx)`
  (is= 3 @(var wilma))
  (is= 3 @#'wilma)) ; Don't do this - it's an abuse of reader macros.

Another note

The (def ...) special form returns the clojure.lang.Var object it creates. Normally, a (def ...) form is used only at the top level in a Clojure source file (or at the REPL), so the return value is silently discarded. However, a reference to the created Var object can also be captured:

(let [p (def five 5)
      q (var five)] 
  (is= clojure.lang.Var
       (type p)
       (type q))

  (is= 6
       (inc five)
       (inc (var-get p))
       (inc (deref q)))

  (is (identical? p q)))

Here we create a global Var five pointing to the number 5. The return value of the def form is captured in the local value p. We use the (var ...) special form to get a reference q pointing to the same Var object.

The first test shows that p and q are both of type clojure.lang.Var. The middle test shows three ways of accessing the value 5. As expected, all retrieve the value 5 which is incremented to yield 6. The last test verifies that p and q both point to the same Java object (i.e. there is only one clojure.lang.Var object that points to the integer 5).

It is even possible for a Var to point to another Var instead of a data value:

(def p (def five 5))   ; please don't ever do this

While it works, I cannot think of a legitimate reason for ever doing this.


There are a couple of good answers already. Just wanted to add this caveat:

(defn f [] 10)
(defn g [] (f))
(g) ;;=> 10
(defn f [] 11)

;; -Dclojure.compiler.direct-linking=true
(g) ;;=> 10

;; -Dclojure.compiler.direct-linking=false
(g) ;;=> 11

So, when direct linking is on, the indirection via a var is replaced with a direct static invocation. Similar to the situation with the handler, but then with every var invocation, unless you explicitly refer to a var, like:

(defn g [] (#'f))

Tags:

Web

Clojure

Var