Generating a class with a macro

33 views
Skip to first unread message

Michel Vollebregt

unread,
Dec 26, 2009, 3:25:38 PM12/26/09
to Clojure
I think I have an interesting problem. I'm trying to create a macro
that generates a class AND a post-init method for the class. Something
like:

;;; create the macro
(defmacro definstance [name & body]
(let [post-init (gensym "postinit")]
`(do
(defn ~(symbol (str "-" post-init)) [this#]
(println "here we'll do something very interesting with
'body' "))
(gen-class
:name ~name
:extends SomeSuperClass
:post-init ~post-init
))))

;;; generate a class with it
(definstance somepackage.MyClass (println "here is the body"))

The macro returns a defn form, for defining the post-init, and a gen-
class form, for defining the class itself. Since a macro can only
return one form, I wrapped the two forms in a single do form.

I can compile the above into a jar. I can then reference it from
another Java project and I can actually use the class. If I run the
Java project from the command line, everything works perfectly.

If I expand the macro with a (macroexpand '(definstance
somepackage.MyClass (println "here is the body"))), put the expansion
in a Clojure source file, and use that from a Java web project (from
within a Servlet) everything still works perfectly.

But when I try to use the above source directly from a web
application, I get an UnsupportedOperationException from the
constructor of the class, because it cannot find a method called
postinit31.

Summarised: the above code works as is in a Java command line
application. The explicit macro expansion works fine in a web
application called from a Servlet. But if I use the above code
literally in a web application, BANG.

It DOES work if I split the single macro into two macros, one for the
gen-class and one for the defn. But that is not what I want.

Can anybody help me out here?

kreso

unread,
Dec 27, 2009, 6:07:21 AM12/27/09
to Clojure
Hi,

Try changing the name of your post-init function to something like
(symbol (str (name cname "-post-init"))), maybe your problem is that
gensym generates different symbols at runtime.

--
Krešimir Šojat

Michel Vollebregt

unread,
Dec 27, 2009, 2:52:55 PM12/27/09
to Clojure
Hmm... seems you're right. If I fix the name of the post-init method,
instead of using a gensym, it works. (Actually, that was exactly what
I was doing to get the two-macro-version working).

Still I don't really understand WHY the gensym is called at different
times. Is this a special case when you use genclass in a macro? Or
should I be aware of other cases too?

But at least I can solve the problem now. Thank you!

kreso

unread,
Dec 27, 2009, 3:17:29 PM12/27/09
to Clojure
> Still I don't really understand WHY the gensym is called at different
> times. Is this a special case when you use genclass in a macro? Or
> should I be aware of other cases too?

What happens is that gen-class (and gen-interface) macroes don't
expand to anything (nil to be exact), but they have side effect as
they generate java bytecode and save them in appropriate files while
expanding and *compile-files* flag is set to true. This is not
something you want to do but in this special case it is requred for
them to behave this way.

So you create unique symbol 'g1 at compile time (or at bytecode
generation time if that is more precise for this situation) that is
used for both your var name and as a argument to :post-init, but at
the runtime your macro is expanded again generating new 'g2 and your
post-init function is assigned to it while gen-class ignores the
second expansion as *compile-files* is set to false and you load
bytcode generated from the time when gensym generated 'g1. It works
from time to time because gensym can generate the same unique symbol
in different processes.

--
Krešimir Šojat

Michel Vollebregt

unread,
Dec 27, 2009, 5:04:51 PM12/27/09
to Clojure
That is a very clear explanation. Thanks!
Reply all
Reply to author
Forward
0 new messages