为什么将 compojure 路由定义为宏?

why are compojure routes defined as macros?

例如 Luminus 网站 states

Compojure route definitions are just functions that accept request maps and return response maps...

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

但是复合路由是不是函数

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

可以使用 returns 函数的函数 make-route,但不允许解构。因此,作为一个函数,您不能使用 compojure 的特殊语法来进行解构(即向量),但这会阻止任何形式的解构吗?宏是否给了他们性能提升?

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

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

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

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

如果 GET 是一个函数,所有 index* 符号,加上 viewgreeting 都必须被引用:

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

由于函数参数在函数被调用之前计算,(index) 将在表单被读取后立即计算。此外,greeting arg 将在每次 HTTP 请求时发生变化,因此显然无法提前知道。

该宏还处理所有解构魔法,这在常规函数中(通常)是不可能的。

经常令人困惑(并且没有很好地向初学者解释)的是像这样的一行:

(GET "/:greeting/" [greeting] (view greeting))

不正常"Clojure code"。相反,它是一种 shorthand(领域特定语言或准确地说是 DSL),GET 宏将摄取并用作有关如何生成 "legal" Clojure 代码的说明。 DSL 通常比最终生成的代码更短、更简单、更方便人类使用,就像 Clojure 比 Clojure 编译器或机器生成的 Java 字节码更短、更简单、更方便一样最终由 JVM 生成的汇编语言代码。

简而言之,每个宏都是一个 "pre-compiler",它将 DSL 转换为纯 Clojure,然后由 Clojure 编译器摄取以生成 Java 字节码。

话虽如此,它 可以 重新安排以将所有宏魔法放入 defroutes 宏因此 GET 符号既不是函数也不是宏,而只是实现中的 :get 关键字一样的一种标记。作为用户,这些实现细节通常并不重要。

更新

最好只在某个函数不起作用或非常笨拙时才使用宏。决定因素通常是是否要使用裸(未引用)符号,但不提前评估它们。 Core Clojure 本身对许多其他语言中 "built-ins" 的结构使用宏,包括 defnforandorwhen、和别的。

另请注意,宏 不能 做一些函数可以做的事情,例如作为像 filter.[=30= 这样的高阶函数的参数]

综上所述,一个函数定义了一个行为。一个宏定义了一个语言扩展.