I'm confused with how symbols are evaluated inside the macro. I tried the following example
(defmacro fx-bad
[f x]
`(f x))
(defmacro fx
[f x]
`(~f ~x))
(let [f inc x 1] (fx f x)) ;-> 2
(let [f inc x 1] (fx-bad f x)) ;-> exception
fx
macro functions correctly, whereas fx-bad
throws the exception
CompilerException java.lang.RuntimeException: No such var: user/f, compiling:(/tmp/form-init2718774128764638076.clj:12:18)
Are the symbols resolving inside the macro? Why fx-bad
doesn't work but fx
does?
--
Edit:
Apparently the exception has something to do with namespaces. Actually no arguments are ever evaluated in the macro. ~
in the syntax quote just produces the actual string (Symbol) passed to macro, without it the symbol inside the list is returned as it is.
Interesting thing is is, if the arguments supplied to macro call and symbols inside the quoted (not syntax quoted) list have equivalent names, they doesn't have to be unquoted, they are the same symbol anyway. This is good indication, how macro takes place before the evaluation and just manipulates raw symbols which doesn't mean anything at this point.
However, with the syntax quote case is different, and exception is thrown until symbols are unquoted, even thought the expanded macro looks like a valid line of code for evaluator to evaluate. Here are some examples
(defmacro fx
[f x]
`(~f ~x))
(defmacro fx-bad
[f x]
'(f x))
(defmacro fx-very-bad
[f x]
`(f x))
`(let [f inc x 1] ~(macroexpand '(fx f x)))
`(let [f inc x 1] ~(macroexpand '(fx-bad f x)))
`(let [f inc x 1] ~(macroexpand '(fx-very-bad f x)))
(macroexpand '(fx (fn [a] a) b))
(macroexpand '(fx-bad (fn [a] a) b))
(macroexpand '(fx-very-bad (fn [a] a) b))
(let [f inc x 1] (fx f x)) ;-> 2
(let [ff inc xx 1] (fx ff xx)) ;-> 2
(let [f inc x 1] (fx-bad f x)) ;-> 2
;(let [ff inc xx 1] (fx-bad ff xx)) ;-> exception
;(let [f inc x 1] (fx-very-bad f x)) ;-> exception
--
=> #'user/fx
#'user/fx-bad
#'user/fx-very-bad
(clojure.core/let [user/f clojure.core/inc user/x 1] (f x))
(clojure.core/let [user/f clojure.core/inc user/x 1] (f x))
(clojure.core/let [user/f clojure.core/inc user/x 1] (user/f user/x))
((fn [a] a) b)
(f x)
(user/f user/x)
2
2
2
So what is actually happening here, why the exception is thrown in the case of syntax quote?