I am going through the clojure web development book and it tells me to pass the handler (defined bellow) var object instead of the function itself because the function will change dynamically (this is what wrap-reload does).
The book says:
"Note that we have to create a var from the handler in order for this middleware to work. This is necessary to ensure that the Var object containing the current handler function is returned. If we used the handler instead then the app would only see the original value of the function and changes would not be reflected." I don't really understand what this means, are vars similar to c pointers?
(ns ring-app.core (:require [ring.adapter.jetty :as jetty] [ring.util.response :as response] [ring.middleware.reload :refer [wrap-reload]])) (defn handler [request] (response/response (str "/ your IP is: " (:remote-addr request) ""))) (defn wrap-nocache [handler] (fn [request] (-> request handler (assoc-in [:headers "Pragma"] "no-cache"))))
Here is the handler call:
(defn -main [] (jetty/run-jetty (wrap-reload (wrap-nocache (var handler))) {:port 3001 :join? false}))
Yes, the var is similar to a C pointer. This is poorly documented.
Suppose you define 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:
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.
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))