3
votes

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,


(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*.

3

3 Answers

5
votes

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.

4
votes

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.

1
votes

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