Search code examples
clojuremacrosquoteparentheses

Trouble with clojure quote-paren `( ... ) macro


For practice, I've defined

(defmacro quote-paren
  "body -> `(body)"
  [& body]
  `(~@body))

which has the expected transformation (quote-paren body) => ``(body)`. It seems to satisfy a few basic tests:

user=> (macroexpand-1 `(quote-paren 3 4 5))
(3 4 5)
user=> (macroexpand-1 `(quote-paren println "hi"))
(clojure.core/println "hi") 
user=> (macroexpand-1 `(quote-paren (println "hi")))
((clojure.core/println "hi"))

However, I've been testing it with this do-while macro (modified from here):

(defmacro do-while
  [test & body]
  (quote-paren loop [] 
    ~@body
    (when ~test
      (recur))))

(def y 4)
(do-while (> y 0)
  (def y (dec y)))

But the result is

IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote-splicing  clojure.lang.Var$Unbound.throwArity (Var.java:43)

I don't understand this, because from what I can see the `quote-paren' macro works fine (with ~@body plugged in):

user=> (macroexpand-1 
         `(quote-paren loop [] 
            (def y (dec y))
            (when ~test
              (recur))))

(clojure.core/loop [] (def user/y (clojure.core/dec user/y)) (clojure.core/when #<core$test clojure.core$test@1f07f672> (recur)))

But trying to macroexpand do-while causes an "unbound fn". Is there something subtle I'm missing?


Solution

  • missing the syntax-quote before quote-paren

    user> (defmacro do-while
      [test & body]
      `(quote-paren loop [] 
        ~@body
        (when ~test
          (recur))))
    #'user/do-while
    

    which then expands properly:

    user> (macroexpand '(do-while (> y 0)
                          (def y (dec y))))
    (loop* [] (def y (dec y)) (clojure.core/when (> y 0) (recur)))
    

    and seems to work:

    user> (def y 4)
    #'user/y
    user> (do-while (> y 0)
      (def y (dec y)))
    nil
    user>