在Clojure中,如何用自身定义defmacro?

7

我一直在查看使用“let”定义的defmacro源代码:

(def

 ^{:doc "Like defn, but the resulting function name is declared as a
  macro and will be used as a macro by the compiler when it is
  called."
   :arglists '([name doc-string? attr-map? [params*] body]
                 [name doc-string? attr-map? ([params*] body)+ attr-map?])
   :added "1.0"}
 defmacro (fn [&form &env 
                name & args]
             (let [prefix (loop [p (list name) args args]

然而,“let”本身就被定义为一个宏:
(defmacro let
  "binding => binding-form init-expr

  Evaluates the exprs in a lexical context in which the symbols in
  the binding-forms are bound to their respective init-exprs or parts
  therein."
  {:added "1.0", :special-form true, :forms '[(let [bindings*] exprs*)]}
  [bindings & body]
  (assert-args
     (vector? bindings) "a vector for its binding"
     (even? (count bindings)) "an even number of forms in binding vector")
  `(let* ~(destructure bindings) ~@body))

有人能解释一下这是怎么工作的吗?我不明白如何在需要“defmacro”已经被定义的情况下定义“defmacro”。(如果这讲得通的话 :)

2个回答

8
这是可能的,因为在 core.clj 中定义 defmacro 函数之前已经有了 let 的定义,位于 此位置(稍后会重新定义)。宏只是普通函数,它们绑定的变量具有元数据键 :macro,值为 true,这样在编译时编译器就可以区分宏(在编译时执行)和函数,如果没有这个元数据键,就无法区分宏和函数,因为宏本身是一个处理 S 表达式的函数。

2
(fn* let [&form &env & decl] (cons 'let* decl)) 这段代码定义了 let 函数。它使用了已经在 Clojure 的 Java 代码中定义好的 let* 函数。 - Ankur

5

递归宏在Clojure语言核心和其他程序中的许多地方运行良好。 宏只是返回S-表达式的函数,因此它们可以像函数一样递归。在您的示例中,let实际上调用了一个不同的函数let*(在函数名称中使用*是可以的),因此虽然递归宏很好,但这并不是它们的一个例子。


2
这个问题不是关于明显的循环定义吗? - Jeremy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接