2017-06-21 49 views
6

例如Luminus公司网站states为什么compojure路由被定义为宏?

的Compojure路由定义只是接受请求 地图和返回响应地图功能...

(GET "/" [] "Show something") 
... 

不过的Compojure路线是功能

(defmacro GET "Generate a `GET` route." 
    [path args & body] 
    (compile-route :get path args body)) 

可以使用函数make-route返回一个函数,但不允许解构。所以作为一个函数,你不能使用compojure的特殊语法来破坏(即向量),但是这是否会阻止任何形式的解构?宏从给他们的表现增加?

(make-route :get "/some_path" some_handler) 

不能使用宏包装函数将析构语法传递给函数吗?

回答

7

使用宏的一个原因是用户可以编写函数和符号名称而不必引用所有内容。就拿这个例子from the Clojure Cookbook

; Routing 
(defroutes main-routes 
    (GET "/" [] (index)) 
    (GET "/en/" [] (index)) 
    (GET "/fr/" [] (index-fr)) 
    (GET "/:greeting/" [greeting] (view greeting))) 

所有index*符号,加上viewgreeting将不得不如果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等高阶函数的参数。

总之,一个函数定义了一个行为。宏定义了语言扩展

+0

如果GET是一个函数,那么''(view greeting)'根本就没有意义。你需要写一些更像'(GET“/:greeting”(fn [req](let-request [[greeting] req](view greeting))))',这很麻烦但不是不可能的。 – amalloy

+0

函数方法需要对大多数特性使用'eval',这就是宏观解决方案非常受欢迎的原因。 –

+0

@AlanThompson--纯粹好奇。我不是宏的狂热粉丝,因为我相信他们经常会对真正发生的事情产生误解。当项目大量使用它们时(设计决策等),始终感兴趣。刚刚发现“bidi” - 数据结构方法在精神层面上对我说。 – beoliver

相关问题