简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM