unquote a list in Clojure macro
I try to define a macro to extract info from a special form of text, [0 => "0n"]
, actually a list in Clojure.
Now let's say I simply want to get the first
part of it with macro get-input
.
(println (get-input [0 => "0n"])) ; i.e. 0
The one below works pretty well.
; this works
(defmacro get-input
[expr]
(let [input (first expr)]
input))
But when I use Syntax-Quote , ie, backquote, things are getting confusing. Just unquoting the expr
with ~
leads me to this. While actually I never use the second
part of the expr
, ie =>
, but seems it still get evaluated behind.
CompilerException java.lang.RuntimeException: Unable to resolve symbol: => in this context
; this sucks
(defmacro get-input
[expr]
`(let [input# (first ~expr)]
input#))
I want to know what is the difference between first solution and the second one.
Macro are expanded at compile time, and the body of the macro is evaluated. If it is syntax-quoted, only the unquoted expressions are evaluated, but if there are no quote (like in your first macro definition), the body is evaluated at compile time.
If you expand (get-input [0 => "0n"])
with your first definition (no quote), you will have
> (macroexpand '(get-input [0 => "0n"]))
0
0
is the result of the macro-expansion, meaning all calls to (get-input [0 => "0n"])
will be replace at compile time by 0
.
With the second definition the expansion will be something like (the generated symbol will be different)
> (macroexpand '(get-input [0 => "0n"]))
(let* [input__11966__auto__ (first [0 => "0n"])]
input__11966__auto__)
After expansion, the Clojure compiler will evaluate the vector [0 => "0n"]
, and as stated in the clojure doc states:
Vectors, Sets and Maps yield vectors and (hash) sets and maps whose contents are the evaluated values of the objects they contain.
Each element of the vector are evaluated in sequence, which is fine for 0
(evaluates to the number 0), but not for =>
which is not a known symbol.
May be what you are looking for is to expand the expression (in your case the vector), without evaluation of its content. For this you need to quote
the result of the ~
, eg
(defmacro get-input-as-str
[expr]
`(let [a# (quote ~expr)]
(map str a#)))
which expand to - note the '[...]
:
> (macroexpand '(get-input-as-str [0 => "0n"]))
(let* [a__12040__auto__ '[0 => "0n"]] (map str a__12040__auto__))
And gives
> (get-input-as-str [0 => "0n"])
("0" "=>" "0n")
You're getting this exception because you're trying to unquote [0 => "0n"]
form, which is not a valid Clojure code. You could fix it by changing an order of first
and unquote operations:
(defmacro get-input
[expr]
`(let [input# ~(first expr)]
input#))
链接地址: http://www.djcxy.com/p/65768.html
上一篇: Clojure宏:引用和语法引用
下一篇: 在Clojure宏中取消引用列表