在Clojure宏中评估修改参数的惯用方法是什么?

时间:2015-03-03 11:04:51

标签: clojure macros arguments eval

想象一下,我的意图是修改传递给宏的s表达式的语法树,修改语法树,然后评估修改后的参数。我可以做类似以下的事情:

(defmacro runargs [args]
  (eval (cons (first args) " buckeroo")))

(runargs (println "hi there"))

现在我觉得感觉惯用,因为我在代码中间打了个大eval

现在我稍微修改它并提出以下内容:

(defmacro runargs [args]
  `(~@(cons (first args) " buckeroo")))

(runargs (println "hi there"))

这解决了eval问题。但是,我仍然觉得这不是很惯用的。

我的问题是:在Clojure宏中评估修改过的参数的惯用方法是什么?

1 个答案:

答案 0 :(得分:2)

你给出的两个例子完全不同。第一个是在宏展开时间评估修改后的s-expression,这几乎肯定不是你想要的。

user=> (defmacro runargs-eval [args]
         (eval (cons (first args) " buckaroo")))
#'user/runargs-eval

user=> (macroexpand '(runargs-eval (println "hi there")))
  b u c k e r o o
nil

user=> (defmacro runargs [args]
         `(~@(cons (first args) " buckeroo")))
#'user/runargs

user=> (macroexpand '(runargs (println "hi there")))
(println \space \b \u \c \k \e \r \o \o)

如果您只是简单地评估恰好包含对宏的调用的单个s表达式,但是如果您正在编译使用宏的代码(如在lambda),宏扩展发生在编译时:

user=> (defn say-hello-eval [x] (runargs-eval (println x)))
  b u c k e r o o
#'user/say-hello-eval

user=> (say-hello-eval "hi there")
nil

user=> (defn say-hello [x] (runargs (println x)))
#'user/say-hello

user=> (say-hello "hi there")
  b u c k e r o o
nil

宏只是接受未评估表达式的函数,并返回修改后的未计算表达式。如果你真的想要将表达式作为宏展开的一部分进行评估,这将是一个非常不寻常的情况 - 通常你的宏只会返回修改后的表达式,并让Clojure编译器在适当的时候进行评估。

第二个示例中的语法引用并不是必需的 - 语法引用的一般用例是当您将宏参数插入到模板化表达式中时,该表达式包含应解析为命名空间中的某些内容的符号。宏是定义的。如果你的宏真的是一个s表达式的语法转换,那么你可以像处理任何其他Clojure数据结构一样对表达式进行操作:

(defmacro runargs [args]
  (cons (first args) " buckeroo"))