Like some other posters to this group, I thought it would be cool to have Clojure create a GUI based on a declarative definition. My plan was to use a recursive macro to handle the creation of nested swing objects and I found myself wanting to view the macro expansions. Unfortunately macroexpand and macroexpand won't do this for nested macros:
Note neither macroexpand-1 nor macroexpand expand macros in subforms (http://clojure.org/macros#toc12) For example: user=> (macroexpand '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (if (and (= a b) (= c d)) true (cond (or (= e f) (= g h)) false)) The initial 'cond' has been expanded into an 'if', but cond is a recursive macro and the expansion produces another 'cond'. Furthermore the 'and' & 'or' macros are unexpanded. So, I wrote a function to produce a full macro expansion. I guess this has been done a million times for other lisps, but since I'm a lisp novice I had some fun doing it myself. After several ugly attempts (which were educational in their own right) I came up with the following set of functions. (defn mapr "recursively maps a function to a sequence and subsequences within the map results." [f & sequence] (defn maprec [form] (if (seq? form) (map maprec (f form)) form)) (first (maprec sequence))) mapr maps a supplied function over a sequence and then maps the same function over nested subsequences in the resulting collection. The inner function maprec is interesting because it recurses by passing itself to map. This is probably heavy on the stack, but that shouldn't be a problem for my purposes. Now, with mapr, it's trivial to expand all the macros within a form. Simply call mapr passing the macroexpand function and the form to be expanded. (defn macroexpand-r "Expands all macros in a form and its subforms." [forms] (mapr macroexpand forms)) Repeating the above example with macroexpand-r: user=> (macroexpand-r '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (let* [and__196 (= a b)] (if and__196 (= c d) and__196)) true (if (let* [or__202 (= e f)] (if or__202 or__202 (= g h))) false nil)) All the 'cond's 'and's & 'or's have been replaced with the 'if' and 'let' special forms. Pretty ugly isn't it? That's useful, but too much since I will usually only be interested in a single macro. I will want to expand instances of that macro and leave others unexpanded. I can do that by passing mapr a function which conditionally expands macros: user=> (mapr (fn [f] (if (= 'cond (first f)) (macroexpand f) f)) '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (if (and (= a b) (= c d)) true (if (or (= e f) (= g h)) false nil)) Now the cond has been expanded (twice) but the 'and' & 'or' remain. Just what I want, except I can't be bothered writing the fn everytime so: (defn macroexpand-only "Expands all macros in a form and its subforms that match a given symbol." [macro forms] (mapr (fn [f] (if (= macro (first f)) (macroexpand f) f)) forms)) user=> (macroexpand-only 'and '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (cond (and (= a b) (= c d)) true (let* [or__202 (= e f)] (if or__202 or__202 (clojure/or (= g h)))) false) user=> (macroexpand-only 'or '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (cond (let* [and__196 (= a b)] (if and__196 (clojure/and (= c d)) and__196)) true (or (= e f) (= g h)) false) user=> (macroexpand-only 'cond '(cond (and (= a b) (= c d)) true (or (= e f) (= g h)) false)) -> (if (and (= a b) (= c d)) true (if (or (= e f) (= g h)) false nil)) Oops, looks like I've a got a problem. macroexpand sometimes produces qualified names in the expansions and sometimes doesn't: user=> (macroexpand '(cond (= a b) true (= c d) false)) -> (if (= a b) true (cond (= c d) false)) user=> (macroexpand '(or (= a b) (= c d))) -> (let* [or__202 (= a b)] (if or__202 or__202 (clojure/or (= c d)))) There is an unadulterated recursive cond in the expansion of the cond form. But the expansion of the 'or' form contains the 'closure/or' symbol. This is because the definition of cond quotes its recursive call while the 'or' (and 'and') definitions don't. Is this a bug? or if not can someone explain the different usage? And while I'm asking questions, why don't macroexpand and macroexpand-1 expand macros in subforms? The macroexpand-only function could be modified to accommodate the different representations of macros, but I'll leave that for another day. And that's it. If you're going to use these macros, be warned, they're only minimally tested and I don't know what I'm doing. I wouldn't be at all surprised if there's a good reason why Rich hasn't made something like this part of Clojure. (or maybe he has and I just didn't notice) Now, where was I ... --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---