在我看来,最有用的直觉来自于理解给定运算符 / Var 的目的。设计良好的宏根本不能写成函数,并且仍然以相同的语法提供相同的功能,因为如果可以的话,它们实际上会被写成函数(参见上面的“精心设计”部分!)。1因此,如果您正在处理一个不可能是常规函数的构造,那么您就知道它不是;否则很可能是。
此外,了解库导出的 Var 的常用方法会告诉您预先处理的是宏还是函数。对于doc
((doc foo)
表示foo
如果确实是这种情况,这是一个靠近其输出顶部的宏),source
(因为它为您提供了整个代码) 和M-.(使用 nrepl.el 或 swank-clojure 跳转到 Emacs 中的定义;M-,跳转背部)。文档可能会提及什么是宏,什么不是(除非文档字符串不一定如此,因为访问文档字符串的所有常用方法都已经告诉您是否正在处理宏,如上所述)。
如果您浏览一段代码的目的是粗略理解它可能在假设各种运算符执行其名称所暗示的功能的情况下可能会做什么,那么(1)这些名称具有足够的暗示性并且您会得到了解代码的意图,因此您甚至不需要关心哪些运算符恰好是宏,或者 (2) 名称的暗示性不够,因此您需要深入研究文档或源代码无论如何,一些运算符,然后您将学习的第一件事是它们中的哪些被注册为宏。
最后,宏没有单一的命名风格,尽管有一些特定于特定用例的约定。例如with-foo
-style 结构往往是便利宏,其目的是简化对 type 资源的处理foo
;dofoo
-风格的结构往往是宏,它需要执行一组表达式(多少次以及设置额外的上下文取决于宏;这个家族中最基本的成员,do
实际上是一种特殊形式而不是宏); deffoo
-style 构造引入了新的 Var 或类似类型的实体。
值得指出的是,类似的模式有时会被打破。例如,大多数线程构造 ( ->
& Co.) 是宏,但xml->
fromclojure.data.zip.xml
是一个函数。当考虑到所提供的功能时,这是完全有道理的,这让我们回到了运营商作为最有用的直觉来源的目的这一点。
1此规则可能有一些例外情况。人们会期望这些被记录在案。当然,有些项目根本没有记录(或几乎没有记录);在这里,问题完全消失了,因为无论如何都必须找到源头才能理解事物。