Clojure宏扩展

时间:2011-05-13 01:36:10

标签: macros clojure lisp

我正在研究一个宏,我正在试图弄清楚如何避免某些形式的扩展,例如采取以下和宏,


(defmacro and
  ([] true)
  ([x] x)
  ([x & next]
   `(let [and# ~x]
      (if and# (and ~@next) and#))))

扩展时,

(mexpand-all '(and 1 2 3))

变,


(let* [and__973__auto__ 1]
      (if and__973__auto__
        (let* [and__973__auto__ 2]
              (if and__973__auto__ 3 and__973__auto__))
        and__973__auto__))

在这种情况下,我需要做的就是停止扩展到let *。

3 个答案:

答案 0 :(得分:5)

咦?目前尚不清楚“停止”let的扩展意味着什么。 let是clojure.core中定义的宏,编译器对此一无所知:它只能理解let*。如果您的宏扩展为let(以某种方式)拒绝进一步扩展,则无法编译。

如果您想单独检查宏的输出,而不必担心递归扩展它,则应使用macroexpandmacroexpand-1代替此mexpand-all。我不知道mexpand-all来自哪里,但当我需要这样的东西时,我会使用clojure.walk/macroexpand-all

答案 1 :(得分:4)

递归宏扩展通过重复扩展表单直到没有要扩展的宏来工作。这意味着如果你想以递归方式扩展宏,但忽略某些形式,你必须编写自己的自定义扩展器代码,或者找到别人的代码。

这是一个简单的例子:

(defn my-expander [form]
    (cond (not (list? form)) (mexpand-1 form)
        (= (first form) 'let) form
           :else (map my-expander (mexpand-1 form))))

如果我犯了任何错误,请原谅我。 Scheme和CL比Clojure强得多。

- Edit-- 请注意,上述函数也不会扩展let语句的子表单。

答案 2 :(得分:1)

使用macroexpand-1执行单级宏扩展。

加载and宏后,此表达式为:

user=> (macroexpand-1 '(and 1 2 3))

收率:

(clojure.core/let [and__1__auto__ 1] (if and__1__auto__ (clojure.core/and 2 3) and__1__auto__))