Chas Emerick
unread,Jun 26, 2008, 10:56:18 AM6/26/08Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Sign in to report message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to Clojure
At some point, I came to need to define a generated Java class that
had methods that returned instances of the class being generated. The
gen-and-save-class form looked like this:
(gen-load-save-class
"."
'pkg.Foo
:implements [Bar]
:init 'new-foo
:constructors {[(. Float TYPE)] []}
:methods [['union [java.util.Collection] 'pkg.Foo]] ;<---
:state 'state)
The problem is with the usage of 'pkg.Foo in the union method
signature def. gen-class expects classes for those signature
definitions, and since the pkg.Foo class doesn't exist yet, I can't
reference it in the signature.
On a lark, I thought I'd try a macro that would generate and load a
class (without any references to the generated class), and then
generate and save that same class with all of the method definitions,
etc. (By no means efficient, since the bytecode is being generated
twice, but this is mostly fun and I didn't want to futz with using gen-
class directly and then handling writing out or loading the bytecode.)
The result is this:
(defmacro gen-load-save-class [path name & options]
(let [optmap (apply hash-map options)
save-only-opts #{:methods :constructors}
trimmed-opts (reduce #(if (save-only-opts %2) %1 (concat %1
[%2] [(optmap %2)])) [] (keys optmap))]
`(do (gen-and-load-class ~name ~@trimmed-opts)
(eval '(gen-and-save-class ~path ~name ~@options)))))
gen-load-save-class has the same signature as gen-and-save-class. It
pulls out the options to gen-class that can contain self-references
(only :methods and :constructors at the moment, I believe), generates
and loads the result of that trimmed-down option set, and then
generates and saves the full class definition.
One interesting wrinkle is that the gen-and-save-class form needs to
be wrapped in an eval; otherwise clojure will attempt to resolve
references to the generated classname in the options to gen-and-save-
class before it even gets to the point of evaluating the gen-and-load-
class call.
I know Rich is aware of this use-case (referring to the generated
classname when using gen-class), so a real solution will probably be
forthcoming sooner or later, but this was an interesting exercise in
the meantime.
- Chas