Clojure macros: quoting and syntax quoting
Say I have the following code:
(defmacro test1 [x]
(list 'fn '[y]
(if (pos? x)
'(println y)
'(println (- y)))))
It does what I need, composes a function based on x, and leaves no references to x. For example, (test1 1)
macroexpands into (fn* ([y] (println y)))
.
Now, I'd like to rewrite it using syntax quoting. This is what I have so far:
(defmacro test2 [x]
`(fn [y#]
(if ~(pos? x)
(println y#)
(println (- y#)))))
This does exactly the same, with one exception: it leaves an (if true ..)
expression in the expanded expression:
(fn* ([y__12353__auto__] (if true (clojure.core/println y__12353__auto__) (clojure.core/println (clojure.core/- y__12353__auto__)))))
This might not be an issue if the compiler can optimize it out. Still, is there a way I could omit it?
When you use test2
it will unquote the whole form (pos? x)
which will work at compile time if it's a constant number or perhaps a gloabl that is already defined, but not if you pass a lexically scoped variable name that doesn't exist yet.
Thus, you really want this instead:
(defmacro test2 [x]
`(fn [y#]
(if (pos? ~x) ; just unquote x, not the whole predicate expression
(println y#)
(println (- y#)))))
(macroexpand '(test2 y))
; ==>
; (fn* ([y__1__auto__]
; (if (clojure.core/pos? y)
; (clojure.core/println y__1__auto__)
; (clojure.core/println (clojure.core/- y__1__auto__)))))
(defn test-it []
(let [y -9]
(test2 y)))
((test-it) 5) ; prints "-5"
Feel free to try this with your version. (hint: You'll get an Exception since clojure.lang.Symbol cannot be cast to java.lang.Number)
UPDATE
Since you want to make the function based on a constant you need to write it a little differently:
(defmacro test3 [x]
(assert (number? x) "needs to be a compile time number")
(if (pos? x)
`(fn [y#] (println y#))
`(fn [y#] (println (- y#)))))
Now you'll get an error if you use (test3 x)
since x
is not a number but get what you want when you evaluate (test3 -10)
since -10
is a number we can work with compile time. I'm not sure you'll notice a speed improvement though since these are hardly heavy algorithms.
上一篇: 为什么这个clojure宏需要“〜”?
下一篇: Clojure宏:引用和语法引用