【问题标题】:Make macros and functions integrate more seamlessly使宏和函数的集成更加无缝
【发布时间】:2011-03-11 01:44:31
【问题描述】:
好的,我非常了解如何使用函数和宏。
我很好奇的是为什么编译器在集成两者时不能更聪明一点,例如考虑 Clojure 代码:
(defmacro wonky-add [a b] `(+ ~a (* 2 ~b)))
(defn wonky-increment [a] (apply wonky-add a 1))
=> Error: can't take value of a macro
是的,我知道我可以通过取出“应用”来完成这项工作 - 但为什么编译器自己无法弄清楚如何做到这一点?
是否有可能在 Clojure / 其他 LISP 中创建一个版本的 apply 或其他高阶函数,它们与函数和宏作为参数同样适用?
【问题讨论】:
标签:
compiler-construction
functional-programming
macros
clojure
lisp
【解决方案1】:
这个答案不是关于 Clojure,而是关于一般的 Lisp 和宏。
记住:
APPLY 是为了能够使用在运行时创建的参数列表调用函数。
宏用于从一些源代码生成新的源代码 - 生成的源代码将运行。
现在:
如果您允许应用程序在运行时将不同的源代码提供给宏,则需要能够在运行时生成结果代码并执行生成的代码。
因此,在已编译的 Lisp 系统中,每次使用宏调用 APPLY 都可能会创建需要在运行时编译才能执行的新代码。这也意味着当你的代码正在执行并且代码有一些问题时,你可能会在运行时遇到新的编译器错误。因此,要在运行时应用宏,您需要一个编译器和所有必要的信息(例如其他宏)才能扩展和编译代码。
这也意味着在一般情况下,您不能在运行前使用宏编译 APPLY,因为不知道要应用的运行时参数是什么。
在基于解释器的 Lisp 中,可能有更多的信息,并且可以一直应用宏。
在早期的 Lisp 中有正常的函数和所谓的 FEXPR。 FEXPR 允许灵活的调用和运行时代码操作。在 Lisp 历史的后期,它们已被宏取代,因为宏允许高效编译,并且在基于编译器的系统中,允许在运行前处理语法错误。
【解决方案2】:
您所描述的内容听起来像是一流的宏(即:您可以像函数一样操作的宏)。一些 lisps 有这些(例如Arc),但apply 本身不是宏,可能仍然无法工作。