这不是我的“生产代码”,而是为了说明目的而简化了问题。此外,这个问题的标题是误导性的,因为它让人想起〜@扩展,我理解,这可能不一定是问题。如果可以,请建议更好的问题标题。
给定一个具有以下形式的宏:
(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。
有什么方法可以解决这个问题吗?
答案 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:我不是我的代表所以如果你在这个例子中看到一个错误请编辑(或者我会在我回来时修复它)