Clojure - How to make my macro expand before system macros?
Asked Answered
L

3

9

If I do, for example:

(defmacro qqq [] '(toString [this] "Qqq"))
(reify Object (qqq))

it fails because of reify sees (qqq) instead of (toString [this] "Qqq").

The usual solution is a macro that wraps "reify" call with my own thing, but it is longer and more intrusive.

How to make my macros stronger that usual macros to be expanded first?

Expecting something like:

(defmacro ^{:priority 100500} qqq [] '(toString [this] "Qqq"))
(reify Object (qqq))

or

(defmacro qqq [] '(toString [this] "Qqq"))
(expand-first #{qqq} (reify Object (qqq)))
Lazar answered 13/2, 2011 at 13:53 Comment(2)
Pretty sure there is no such mechanism in Clojure.Marker
@Brian Carper, But I suspect expand-first can be implemented externally (and later included in Clojure). Trying to do that.Lazar
L
1

The macro that forces given user macros to expand first (requires clojure.walk):

(defmacro expand-first [the-set & code] 
 `(do ~@(prewalk 
  #(if (and (list? %) (contains? the-set (first %)))
  (macroexpand-all %)
  %) code)))

Who has ideas how to make it better?

Lazar answered 13/2, 2011 at 21:0 Comment(1)
prewalk vs postwalk? macroexpand-all vs just macroexpand?Lazar
D
9

There is a reader-macro to evaluate things at read-time (before macro-expansion time)..

(defn qqq [] '(toString [this] "Qqq"))
(reify Object #=(qqq))

I've never seen this done in "real" code, and I think most people would consider it a hack, but it's there if you need it.

Dismay answered 2/3, 2011 at 18:37 Comment(1)
Shortest version and no additional dependencies. Looks nice. What pitfalls / what #= is normally used for/Lazar
L
1

The macro that forces given user macros to expand first (requires clojure.walk):

(defmacro expand-first [the-set & code] 
 `(do ~@(prewalk 
  #(if (and (list? %) (contains? the-set (first %)))
  (macroexpand-all %)
  %) code)))

Who has ideas how to make it better?

Lazar answered 13/2, 2011 at 21:0 Comment(1)
prewalk vs postwalk? macroexpand-all vs just macroexpand?Lazar
E
0

This will work:

(defn reifyx [x y]
  (eval `(reify ~x ~y)))

(defn qqq [] '(toString [this] "Qqq"))

(reifyx 'Object (qqq))

I found an apply-macro. I tried it, but it seems to be broken. The most important thing I gleaned from looking at apply-macro was that it solved the problem using eval just as I have.

Elaineelam answered 15/2, 2011 at 7:59 Comment(3)
CompilerException Unable to resolve symbol: toString in this contextLazar
Fixed code is (eval `(reify Object ~(macroexpand `(qqq)))) All this "macroexpands" gets duplicated over and over if I use qqq often. Is there better fix?Lazar
Oh, I'm sorry. I forgot to mention. qqq should be a function that returns a list rather than a macro.Elaineelam

© 2022 - 2024 — McMap. All rights reserved.