6
votes

Suppose I define such clojure function:

(defn print-this [this] (println "print this: " this))

If I use the function in repl:

(print-this that)

I would end up with:

print this: that
nil

as output.

Now if I make such definition:

(defn import-by-name [name] (import (some.package.value name)))

and use the function in repl:

(import-by-name "SomeClassName")

I get

java.lang.ClassNotFoundException: some.package.value.name (NO_SOURCE_FILE:0)

where I would expect that "name" be replaced by "SomeClassName" instead. If I type:

(import (some.package.value SomeClassName))

everything works as expected.

Why is it that [name] is not interpreted in the import-by-name function above? Is it possible to dynamically import a java class from a variable value? If so how? thanks!

1

1 Answers

6
votes

import is a macro, so any symbols you pass to it will be taken literally.

(macroexpand '(import (some.package.value name)))

;; => (do (clojure.core/import* "some.package.value.name"))

Literals

For string literals, and by extension collection literals, you can use a macro to accomplish what you are describing.

(defmacro import-by-name [name] `(import '[some.package.value ~name]))

(import-by-name "ClassName") ;; => nil

Vars

For importing classes from a var, you have to start dipping into the internals of namespaces.

(defn import-by-name [n]
  (.importClass (the-ns *ns*)
                (clojure.lang.RT/classForName (str "some.package.value." n))))

There might be a cleaner way, and I will update this answer if I find one.