In clojure, I would like to create a record inside a function.
I tried:
(defn foo []
(defrecord MyRecord [a b])
(let [b (MyRecord. "1" "2")]))
But it causes an exception:
java.lang.IllegalArgumentException: Unable to resolve classname: MyRecord
Any idea?
The key points
You should only use
defrecordat top level.1So, if you do need a custom record type, you should define it outside of
foo(at some point in your code which gets processed beforefoo‘s definition).Otherwise, you could just use a regular map. In particular, if
foowill be creating entities of multiple “types” (at the conceptual level), it probably makes no sense to try to create a record type (a Java class) for each; the natural solution would be to use maps holding a:typekey to indicate the sort of entity represented.Why it doesn’t work
The code from the question doesn’t compile, because Clojure’s compiler resolves class names mentioned as first arguments to
newforms at compile time. ((MyRecord. "1" "2")is expanded to(new MyRecord "1" "2")during the macro expansion process.) Here the nameMyRecordcannot yet be resolved to the appropriate class, because the latter has not yet been defined (it would be created by thedefrecordform afterfoowas first called).To get around this, you could do something horrible like
which causes a kitten to have its
finalizemethod invoked through reflection, resulting in a gruesome death.As a final remark, the above horrible version would sort of work, but it would also create a new record type under the same name at each invocation. This causes weirdness to ensue. Try the following examples for a flavour:
At this point,
(Class/forName "user.Foo")(assuming again that all this happens in theusernamespace) returns the class off3, of which neitherf1norf2is an instance.1 Macros occasionally might output a
defrecordform wrapped in adoalong with some other forms; top-leveldos are special, though, in that they act as if the forms they wrap were individually processed at top level.