开发者

Clojure Macro Expansion

开发者 https://www.devze.com 2023-03-05 07:55 出处:网络
I am working on a macro, I am trying to figure out how to avoid expansion of certain forms, take the following and macro for example,

I am working on a macro, I am trying to figure out how to avoid expansion of certain forms, take the following and macro for example,


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

When expanded,

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

becomes,

开发者_如何学Go

(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__))

In this case what I need to do is stop let from expanding into let*.


Huh? It's not clear what you mean by "stop" let from expanding. let is a macro defined in clojure.core, which the compiler knows nothing about: it only understands let*. If your macro expanded into a let which (somehow) refused to expand further, it would fail to compile.

If you want to inspect just the output of your macro in isolation, without worrying about recursively expanding it, you should use macroexpand or macroexpand-1 instead of this mexpand-all thing. I don't know where mexpand-all comes from, but when I need something like that I use clojure.walk/macroexpand-all.


Recursive macro-expansion works by repeatedly expanding the form until there is no macro to expand. That means that if you want to recursively expand a macro, but ignore certain forms, you'll have to either code your own custom expander, or find someone else's.

Here's a quick example:

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

Please forgive me if I made any mistakes. I'm much stronger with Scheme and CL than Clojure.

--Edit-- Note that the above function will not expand the subforms of a let statement, either.


Use macroexpand-1 to perform a single level of macro expansion.

After loading your and macro, this expression:

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

Yields:

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号