假设我有一堆命名空间(苹果,香蕉,橙色)。在这些命名空间中,我使用eat
宏,它调用(不是“生成”,调用)peel
函数。 peel
函数对于每个水果都是不同的,但宏是相同的,而且相当大,所以我想创建一个包含fruit
宏的eat
命名空间。但是,当我从eat
命名空间调用apple
宏时,eat
宏应调用apple/peel
函数。
为了说明(但这不起作用):
(ns fruit)
(defmacro eat [] (peel))
(ns apple)
(defn peel [] (prn "peeled apple"))
(fruit/eat)
(ns banana)
(defn peel [] (prn "peeled banana"))
(fruit/eat)
要强调,这意味着只有在扩展宏时才应调用剥离函数,如本例所示。
(ns apple)
(defn peel [] (prn "peeled apple"))
(defmacro eat [] (peel))
(macroexpand-1 '(eat))
那么,关于如何组合宏和多态的任何想法?
答案 0 :(得分:7)
您所描述的不是多态,而是所谓的本地捕获。您希望 eat 宏“捕获” peel 的本地定义。
在大多数Lisp中,这被认为是不好的风格,尤其是Clojure,因为它可能导致细微且不可预测的错误。
更好的解决方案是在调用时将正确的剥离传递给 eat 宏:
(ns fruit)
(defmacro eat [peeler] `(~peeler))
(ns apple)
(defn peel [] (prn "Peeled an apple"))
(fruit/eat peel)
如果你真的想进行本地捕获,可以在宏中强制使用〜'(unquote-quote):
(ns fruit)
(defmacro eat [] `(~'peel))
答案 1 :(得分:3)
正如编辑问题中所解释的那样,这与本地捕获略有不同,因为你没有在宏扩展中使用 peel ,而是在宏本身的执行中使用。
这很难,因为宏不会评估他们的论点。即使你将 peel 作为参数传递给 eat ,在宏的主体内它只是一个符号,而不是一个可调用的函数。
执行所需操作(不使用 eval )的唯一方法是在编译时解析符号:
(defmacro eat []
((var-get (resolve 'peel)))
... return the expansion of "eat" ...)
resolve 函数接受一个符号并返回它在当前命名空间中映射到的Var。获得Var后,可以使用 var-get 检索实际函数(Var的值)。额外的括号集调用该函数。
毋庸置疑,这是一个非常不寻常的设计,可能需要重新考虑。
答案 2 :(得分:2)
(defmacro eat [] ((var-get (resolve 'peel))))
请注意,您滥用命名空间。
答案 3 :(得分:1)
简单引用(peel)
为我工作。
(ns fruit)
(defmacro eat [] '(peel))
(ns apple)
(defn peel [] (prn "peeled apple"))
(fruit/eat)
(ns banana)
(defn peel [] (prn "peeled banana"))
(fruit/eat)