3

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 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

What I am trying to do is recursively expand everything except the let form. BTW, mexpand comes from contrib macro-utils
I too am trying to prevent certain forms from expanding. I'd like to provide an explanation for why that problem has a right to exist. In my case I am trying to use clojure macro expansion as a transpiler from a non-clojure lisp to yet another non-clojure lisp. It works quite well except for some corner cases where the source lisp syntax clashes with the clojure's built-in macros.
4

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.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.