import as a macro; dynamic imports?

122 views
Skip to first unread message

Stuart Sierra

unread,
Jan 1, 2010, 2:58:52 PM1/1/10
to Clojure
I should have brought this up before 1.1 was released, but I'm
bothered by the change of clojure.core/import from a function to a
macro.

If I'm creating a namespace dynamically, I can't evaluate the name of
the class I want to pass to import. The only way is to use
undocumented Java functions, like:

(defn import-name
"Import a class named c (a String) into namespace n."
[n c]
(.importClass n (clojure.lang.RT/classForName c)))

Of course, I can write a macro that evaluates the strings, but that's
not really what I want. I want the equivalent of clojure.core/intern
for classes. Is there a better way?

-SS

Rich Hickey

unread,
Jan 2, 2010, 11:46:13 AM1/2/10
to clo...@googlegroups.com

Your example highlights the problem addressed by making import a
macro. Basically, Class.forName can't be proxied, i.e. any indirection
(such as wrapping it in a helper function like you did) breaks the
magic hack inside Class.forName that finds the classloader of the
caller by walking a fixed distance on the stack. Thus, such proxied
imports don't play well with specialized modular classloading
architectures (like OSGi etc) that expect you to be calling
Class.forName for any dynamic use. In such cases, you will be seeing
what the classloader of the wrapping function can see, rather than
what the caller can see.

On the path towards fixing this, I've added a (currently undocumented)
special op - clojure.core/import*, which will emit bytecode for a call
to Class.forName at the point of call (plus all the other stuff needed
to bind in the current namespace). This will ensure correct resolution
in the classloader of the caller. import expands into calls to
import*. Note it is String-based. This too is critical.

user=> (clojure.core/import* "java.util.List")
java.util.List

user=> List
java.util.List

Note that, as a special op, import* still is not a function. The
bottom line is no ordinary function or method can do this job.

If you want to use import* as a workaround, please consider it alpha
and subject to change.

Rich

Reply all
Reply to author
Forward
0 new messages