Reputation: 92076
I have a sequence of records/classes, and I map
over that sequence with new
and expect to get a sequence of instances of those records/classes. I know new
is a special form, but I was expecting Clojure to do The Right Thing in this case.
But this does not work:
(map new [SomeClass1 SomeClass2 SomeClass3])
Neither does this.
(map #(new %) [SomeClass1 SomeClass2 SomeClass3])
Similar code works in Factor.
{ SomeClass1 SomeClass2 SomeClass3 } [ new ] map
What would be the right way to do this in Clojure? (I expect it won't involve Class.newInstance
ugliness.)
Edit:
The following works, but is perhaps slower than necessary. (I don't know for sure. I would appreciate some information on this.)
(map #(eval `(new ~%)) [SomeClass1 SomeClass2 SomeClass3])
Also I am looking for something more elegant.
Upvotes: 4
Views: 204
Reputation: 33657
As new
is a special form, one of the solution to make it work like first class would be use the clojure low level calls like:
(map #(clojure.lang.Reflector/invokeConstructor %1 (into-array [])) [String])
This may lead to the performance problems of Reflection hence the macro based solution are prefered over this.
Upvotes: 1
Reputation: 91587
because special-formes are well... special they are not first class and don't compose as proper functions do, you can solve this with eval and macros:
a solution using eval:
(defn fnew [c] (eval `(new ~c)))
hello.core> (map fnew ['Exception 'java.lang.String])
(#<Exception java.lang.Exception> "")
and a version that takes arguments to the constructors:
(defn fnew [c] (eval `(new ~@c)))
hello.core> (map fnew ['(Exception) '(java.lang.String "I am a contructor argument")])
(#<Exception java.lang.Exception> "I am a contructor argument")
(map fnew [ [Exception] [(class "foo") "I am a contructor argument"]])
(#<Exception java.lang.Exception> "I am a contructor argument")
and here is a macro example
hello.core> (defmacro newify [classes] (apply vector (for [c classes] `(new ~c))))
#'hello.core/newify
hello.core> (macroexpand '(newify [java.lang.String Exception]))
[(new java.lang.String) (new Exception)]
hello.core> (newify [java.lang.String Exception])
["" #<Exception java.lang.Exception>]
The macro version is likely more efficient while the eval version is more flexible.
Upvotes: 3