使用宏的一个原因是用户可以编写函数和符号名称而不必引用所有内容。就拿这个例子from the Clojure Cookbook:
; Routing
(defroutes main-routes
(GET "/" [] (index))
(GET "/en/" [] (index))
(GET "/fr/" [] (index-fr))
(GET "/:greeting/" [greeting] (view greeting)))
所有index*
符号,加上view
和greeting
将不得不如果GET
是一个函数被引用:
; Routing
(defroutes main-routes
(GET "/" [] '(index))
(GET "/en/" [] '(index))
(GET "/fr/" [] '(index-fr))
(GET "/:greeting/" '[greeting] '(view greeting)))
由于在调用函数之前函数参数进行评估,(index)
et al将在表格被读取后立即进行评估。此外,greeting
arg将在每个HTTP请求上发生变化,所以它显然未提前知道。
这个宏也照顾所有的解构魔术,这对于一个普通的函数来说不是(通常)可能的。
其经常混淆(并且不很好地初学者解释)的事情是,像线:
(GET "/:greeting/" [greeting] (view greeting))
不正常“的Clojure代码”。相反,它是一种简写(特定于域的语言或DSL),GET
宏将摄入并用作关于如何生成“合法”Clojure代码的说明。对于人来说,DSL通常比最终生成的代码更加简短,更简单,对于人来说更方便,就像Clojure比Clojure编译器生成的Java Bytecode或机器汇编语言代码最终更简单,更简单由JVM生成。
简而言之,每个宏都是一个“预编译器”,它将DSL变成简单的Clojure,Clojure然后被Clojure编译器接收并生成Java字节码。
话虽如此,它可能被重新安排把所有的宏观幻成defroutes
宏,以便在GET
符号既不是函数也不是宏,而只是一个喜欢的类型标记:get
关键字在执行。作为用户,这些类型的实现细节通常并不重要。
更新
最好是只使用某一功能将无法正常工作或者是很尴尬的宏。决定性因素通常是如果想要使用裸(未加引号)的符号,但不提前对其进行评估。 Core Clojure本身使用宏作为其他语言“内置”的许多构造宏,包括defn
,for
,and
,or
,when
等。
另请注意,宏不能做一些功能可以做的事情,如作为filter
等高阶函数的参数。
总之,一个函数定义了一个行为。宏定义了语言扩展。
如果GET是一个函数,那么''(view greeting)'根本就没有意义。你需要写一些更像'(GET“/:greeting”(fn [req](let-request [[greeting] req](view greeting))))',这很麻烦但不是不可能的。 – amalloy
函数方法需要对大多数特性使用'eval',这就是宏观解决方案非常受欢迎的原因。 –
@AlanThompson--纯粹好奇。我不是宏的狂热粉丝,因为我相信他们经常会对真正发生的事情产生误解。当项目大量使用它们时(设计决策等),始终感兴趣。刚刚发现“bidi” - 数据结构方法在精神层面上对我说。 – beoliver