【问题标题】:Trouble with clojure quote-paren `( ... ) macroclojure quote-paren `( ... ) 宏的问题
【发布时间】:2024-01-15 16:25:01
【问题描述】:

为了练习,我已经定义了

(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”。有什么我遗漏的微妙之处吗?

【问题讨论】:

    标签: clojure macros quote parentheses


    【解决方案1】:

    缺少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> 
    

    【讨论】:

    • 作为旁注:使用 (def y ...) 进行循环控制可能会产生与此问题无关的意外后果 ;-)
    • 嗯,你是对的,但这有点违背了我练习的目的。有没有办法用'(stuff)替换(quote-paren stuff)?简而言之,无需在宏定义中编写`。
    • 我尝试用(quote (stuff)) 替换(quote-paren stuff),但它似乎停止识别变量;那是(defmacro do-while [test &amp; body] (quote (loop [] ~@body (when ~test (recur))))) 并在CompilerException java.lang.RuntimeException: Unable to resolve symbol: body in this context, compiling:(NO_SOURCE_PATH:58) 中运行结果,但是如果最好在另一个问题中解决这个问题,我会理解的。
    • 我在这里回答的第二部分:*.com/questions/12789350/clojure-dynamic-binding 谈到将这样的东西写成一个照应宏(一个引入新名称的宏),您可以使用它来解决取消引用正文的需要(让它解决)
    • something like this 是您的想法吗? (还是不太行,我想我很快就准备放弃了。)