2012-10-09 50 views
7

对于实践中,我定义故障使用Clojure引号括号`(...)宏

(defmacro quote-paren 
    "body -> `(body)" 
    [& body] 
    `([email protected])) 

具有预期的转变(quote-paren 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")) 

不过,我一直是这样做的,而宏(从here修改)测试它:

(defmacro do-while 
    [test & body] 
    (quote-paren loop [] 
    [email protected] 
    (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 [email protected]> (recur))) 

但试图宏观展开do-while导致“unbound fn”。我错过了些微妙的东西吗?

回答

3

缺少语法引号前quote-paren

user> (defmacro do-while 
    [test & body] 
    `(quote-paren loop [] 
    [email protected] 
    (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> 
+1

,只是作为一个侧面注释:使用( def y ...)for loop control可能会产生与此问题无关的意外后果;-) –

+0

嗯,你说的没错,但是那种打败了我锻炼的目的。反正有人用''('stuff')来替换'(quote-paren stuff)'吗?总之,不需要在宏定义中编写'。 – spacingissue

+0

我尝试用'(quote(stuff))替换''(quote-paren stuff)''但它似乎停止识别变量;即'(defmacro做-而 [试验&体] (报价 (环[] 〜@体 (当〜测试 (复发)))))'和运行在'CompilerException java.lang中的结果。 RuntimeException:在这个上下文中无法解析symbol:body,编译:(NO_SOURCE_PATH:58)'如果在另一个问题中最好解决这个问题,我会理解的。 – spacingissue