为了练习,我定义了

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

它具有预期的转换(quote-paren body) =>``(body)`。它似乎满足一些基本测试:
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"))

但是,我一直在使用此do-while宏(从here修改)对其进行测试:
(defmacro do-while
  [test & body]
  (quote-paren loop []
    ~@body
    (when ~test
      (recur))))

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

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

我不明白这一点,因为从我可以看到的'quote-paren'宏正常工作(插入〜@ body):
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)))

但是尝试宏扩展do-while会导致“unbound fn”。有什么我想念的东西吗?

最佳答案

quote-paren之前缺少语法引用

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

然后可以正确展开:
user> (macroexpand '(do-while (> y 0)
                      (def y (dec y))))
(loop* [] (def y (dec y)) (clojure.core/when (> y 0) (recur)))

并似乎可以工作:
user> (def y 4)
#'user/y
user> (do-while (> y 0)
  (def y (dec y)))
nil
user>

10-06 06:37