【发布时间】:2017-11-02 19:00:35
【问题描述】:
这是我写的两个宏
(defmacro hello [x] '(+ 1 2))
&
(defmacro hello [x] (eval '(+ 1 2)))
在第一个宏扩展时,我得到(+ 1 2),而在第二个宏扩展时,我得到值 3。这是否意味着添加发生在编译时?这怎么可能呢?如果我写了一个查询数据库的函数而不是'(+ 1 2) 怎么办?它会在编译时查询数据库吗?
【问题讨论】:
这是我写的两个宏
(defmacro hello [x] '(+ 1 2))
&
(defmacro hello [x] (eval '(+ 1 2)))
在第一个宏扩展时,我得到(+ 1 2),而在第二个宏扩展时,我得到值 3。这是否意味着添加发生在编译时?这怎么可能呢?如果我写了一个查询数据库的函数而不是'(+ 1 2) 怎么办?它会在编译时查询数据库吗?
【问题讨论】:
宏将任意代码注入编译器。通常,其目的是将(1 + 2) 之类的自定义代码“预处理”成Clojure 可以理解的(+ 1 2) 之类的东西。但是,如果您真的愿意,可以将 anything(包括 DB 访问)包含在编译阶段。毕竟编译器只是在通用计算机上运行的一个软件。由于它是开源的,因此您可以直接修改编译器代码来做任何事情。
使用宏只是修改/扩展基本编译器代码的一种更方便的方式,它针对扩展核心 Clojure 语言进行了优化。但是,宏不限于该用例(如果您真的想发疯的话)。
使用 C++ 表达式模板机制也有类似的能力,这是一个图灵完备的编译器预处理器。一个著名的例子是使用编译器将前几个素数计算为“错误”消息。见http://aszt.inf.elte.hu/~gsd/halado_cpp/ch06s04.html#Static-metaprogramming
【讨论】:
宏体在编译期间被执行,它的返回值用来替换它在代码中的使用,这个新的代码将被编译。
因此你的宏代码:
(defmacro hello [x] (eval '(+ 1 2)))
将在编译期间以'(+ 1 2) 的形式实际执行eval,并且该表达式的结果值(3)将作为宏替换其用法的结果(例如(hello 0))返回。
【讨论】: