Lisp / Clojure代码在语法上具有一致性,这是一个加分点,因为不需要理解各种不同的结构。 但有时通过使用不同的语法来查看一段代码更容易理解,例如这是一个切换案例或这是模式匹配构造等没有真正阅读文本。
我几个月前开始使用Clojure,我已经意识到我无法理解代码而不阅读表单的名称,然后使用Google搜索它是宏还是函数以及它是如何工作的。
事实证明,一段Clojure代码,无论语法的一致性如何都不统一。
它可能看起来像一个函数,但如果它是一个宏,那么它可能不会评估它的所有参数。
是否存在所有宏使用的命名约定或缩进样式,以便某人更容易通过名称掌握正在发生的事情?
答案 0 :(得分:4)
在我看来,最有用的直觉来自于理解给定运算符/ Var的目的。精心设计的宏根本无法编写为函数,并且仍然提供相同的功能和相同的语法,如果可以的话,它们实际上将被编写为函数(参见上面“精心设计的”部分!)。 1 所以,如果你正在处理一个不可能是常规函数的构造,那么你知道它不是;否则很可能是。
此外,了解库导出的Vars的常用方法是告诉您是在预先处理宏还是函数。 doc
((doc foo)
表示foo
表示source
是输出顶部附近的宏(如果情况确实如此),with-foo
(因为它会为您提供整个代码)和 M - 。(跳转到Emacs中的定义,使用nrepl.el或swank-clojure; M - ,跳回)。文档可能会提到什么是宏,什么不是(除了文档字符串不一定正确,因为访问文档字符串的所有常用方法已经告诉您是否正在处理宏,如上所述)。 / p>
如果你正在浏览一段代码,意图在假设各个操作符执行其名称所建议的功能的基础上形成对它可能做什么的粗略理解,那么(1)名称是足够暗示的并且您了解代码的用途,因此您甚至不需要关心哪些运算符恰好是宏,或者(2)名称不足以提示,因此您需要深入了解文档或无论如何,一些运营商的来源,然后你将学到的第一件事是注册为宏。
最后,宏没有单一的命名样式,尽管某些约定特定于特定用例。例如foo
- 样式结构往往是方便的宏,其目的是简化处理类型dofoo
的资源; do
- 样式构造往往是宏,它们执行一系列表达式(多少次以及设置的附加上下文取决于宏;该族的最基本成员deffoo
,实际上是一种特殊的形式而不是宏观); ->
- 样式构造引入了新的Vars或类似类型的实体。
值得指出的是,类似的模式有时会被打破。例如,大多数线程构造(xml->
& Co.)都是宏,但clojure.data.zip.xml
中的{{1}}是一个函数。当考虑所提供的功能时,这是完全合理的,这使我们回到关于操作员是最有用的直觉来源的目的。
1 此规则可能有一些例外情况。人们会期望记录这些内容。有些项目当然没有记录(或非常接近);这里的问题完全消失了,因为无论如何必须去源头来理解事物。
答案 1 :(得分:1)
有两个属性通常将宏(或有时特殊形式)与函数区分开来:
第一种情况的示例是let
,letfn
,binding
和with-local-vars
。奇怪的是,defn
被定义为一个函数,但我很确定它与Clojure的引导过程有关(defn
是在定义defmacro
之前定义的。)
第二个例子是and
,or
和lazy-seq
。在所有这些结构中,通过将它们置于条件分支(如if
)或将它们移动到函数体内来懒惰地计算参数。
这两个属性实际上只是宏操纵Clojure语法的表现形式。我不认为线程宏(->
和->>
)非常适合这些类别,但是nil-safe版本(-?>
和-?>>
)类型在懒惰的论点下。
答案 2 :(得分:0)
据我所知,没有强制命名约定。
根据经验,在可能的情况下首选函数,但有时会在遵循模式def<something>
来设置某个内容时发现宏,或者with-<resource>
来执行某些开放资源。
因此,您可能会发现clojure的doc
宏很有用。它将告诉您表单是宏/函数/特殊形式,还是给它的arg列表和文档字符串(如果存在)。例如
(use 'clojure.repl)
(doc and)
将以下内容打印到repl。
clojure.core /和
([] [x] [x&amp; next])
宏
从左到右逐个评估exprs。如果是表格 返回逻辑false(nil或false),并返回该值和 不评估任何其他表达式,否则返回 最后一个expr的值。 (和)返回true。
有些编辑(例如emacs)会将此文档作为弹出窗口或组合键提供,这样可以更快地访问(和阅读)。