Clojure宏难题:在宏args中扩展序列

时间:2011-03-11 19:32:36

标签: clojure

这不是我的“生产代码”,而是为了说明目的而简化了问题。此外,这个问题的标题是误导性的,因为它让人想起〜@扩展,我理解,这可能不一定是问题。如果可以,请建议更好的问题标题。

给定一个具有以下形式的宏:

(defmacro my-add [x & ys] `(+ ~x ~@ys))

现在让我们说我们有一个清单:

(def my-lst '(2 3))

现在我想要一个使用my-add的函数,我可以将my-lst传递给arg,即

(call-my-add 1 my-lst)

我用似乎显而易见的方式定义函数:

(defn call-my-add [x ys]
    (apply my-add (cons x ys)))

可是:

java.lang.Exception: Can't take value of a macro: #'user/call-my-add (repl-1:60)

我已经尝试了各种各样的狂野技巧来使用call-my-add函数来使用evals,apply,甚至将call-my-add定义为宏,但它们都提供类似的ClassCastExceptions。

有什么方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:6)

没有。宏不会,也不会,永远不会访问其参数中包含的实际运行时值,因此无法将它们拼接到扩展中。他们得到的只是你传给他们的符号,在这种情况下my-list。 “解决这个问题的方法”是将my-add定义为函数,然后(可选)有一个宏来调用该函数以生成其代码。

我写了一篇blog post关于这个半近期的文章,你可能会觉得很有启发性。

如果你愿意的话,你可以用evals来做,但几乎在每种情况下这都是一个糟糕的想法:

(let [my-list '(1 2)]
  (eval `(my-add 5 ~@my-list)))

答案 1 :(得分:2)

在Clojure(或我所知的任何Lisp)中显示宏不是一等公民的真实例子。它们不能应用于函数,存储在容器中或传递给函数等。作为交换,它们可以控制何时以及是否评估它们的参数。

在宏扩展时发生的事情必须保持宏扩展时间。因此,如果在宏扩展时评估my-add并且您想要使用apply,那么您需要...另一个宏;申请。

(defmacro call-my-add [x ys]
   `(my-add ~@(cons x ys))) 

宏以这种方式有点传染性。

PS:我不是我的代表所以如果你在这个例子中看到一个错误请编辑(或者我会在我回来时修复它)