For somebody with experience with Lisp of functional programming, all this will seem trivial. But I needed to refresh all this, so here is the result of my small investigation of Clojure macro and how it relates to lazy evaluation and quoting. I also make a few test on the difference between macro and functions.
First I started with a global variable, and a function that produces a side-effect (incrementing the value) and then return the value of the global variable. It is then trivial to see when stuff gets evaluated.
(def glob-1 10)
(defn side-effect  (do (def glob-1 (inc glob-1)) glob-1 ))
Then to clearly see the difference between macro and function I defined these two ones:
(defmacro print-twice [exp] `(do (side-effect)(println ~exp)(println ~exp)))
(defn print-twice-2 [exp] (do (side-effect)(println exp)(println exp)))
Here is the result of the macro:
(print-twice glob-1) "11 11"
(print-twice (side-effect)) "13 14"
We clearly see how the macro expands. In the 2nd call, the side-effect is executed 3 times as expected. We can now try the function:
(print-twice-2 glob-1) "14 14"
(print-twice-2 (side-effect)) "16 16"
We see that
glob-1 is evalued before being passed to the function. As a consequence, 15 is never printed! The side-effect passed as parameter in the 2nd call is evaluated once, before the actual function is run. After these two calls,
glob-1 has a value of 17.
To have a side-effect which doesn’t hard-code the reference to
glob-1, we can turn it into a macro that takes a variable as parameter.
(defmacro side-effect-1 [v] `(do (def ~v (inc ~v)) ~v ) )
The result are (of course) then similar:
(def glob-1 20)
(print-twice glob-1) "21 21"
(print-twice (side-effect-1 glob-1 )) "23 24"
(print-twice-2 glob-1) "24 24"
(print-twice-2 (side-effect-1 glob-1 )) "26 26"
OK, that’s great, but quasi-quoting isn’t limited to macro. I can also do it in a function.
(defn print-twice-3 [exp] `(do (side-effect)(println ~exp)(println ~exp)))
The result of this function is an expression. It needs to be evaluated to produce the actual result.
(def glob-1 30)
(eval (print-twice-3 glob-1)) "30 30"
(eval (print-twice-3 (side-effect))) "32 32"
We clearly see here again that the semantic of the parameters is not the same between macro and function. The parameter
glob-1 is evaluated before being passed to the function, and as a consequence
~exp expands twice to a literal value of 30, not the symbol
glob-1 as in a macro, and we don’t get “31 31” as we would with the macro. The same happens for the 2nd call.
Other links related to macro and meta-programming: