Search code examples
clojuremacrosrecord

Dynamic records in Clojure


When I try to run the following code from the REPL (playing with dynamic records):

(defrecord (symbol "rec2") (vec (map symbol ["f1" "f2"])))

I receive the error CompilerException java.lang.ClassCastException: clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol, compiling:(NO_SOURCE_PATH:23)

I'm asking myself where I generate this PersistentList considering that:

user=> (symbol "rec2")
rec2
user=> (vec (map symbol ["f1" "f2"]))
[f1 f2]

But my real question is why the following code works:

user=> (defrecord rec2 [f1 f2])
user.rec2

I've also tried:

user=> (clojure.core/defrecord (clojure.core/symbol "rec1") (vec (clojure.core/map clojure.core/symbol ["f1" "f2"])))
CompilerException java.lang.RuntimeException: Can't refer to qualified var that doesn't exist, compiling:(NO_SOURCE_PATH:40)

(qualified var? The only difference is that I'm fully qualifying function names WHICH EXIST, BTW) Obviously I'm missing something in my understanding of Clojure defrecord macro, but I thought that macro are simply AST modifiers so if I give it a symbol or something that resolves to a symbol it's the same thing, so I would like someone to explain me why the normal form works and the others not!

TIA!


Solution

  • The problem is following: defrecord is a macro and all arguments are not evaluated. They passed to macro, so it gets (symbol "rec2") - list that contains 2 elements: symbol and string, not rec2 as you expect. You can try following:

    (eval `(defrecord ~(symbol "rec2") ~(vec (map symbol ["f1" "f2"])))) 
    

    It creates list (defrecord rec2 [f1 f2]) and then evaluates it.
    But I don't think that it's good idea to eval some code dynamically. May be there is some other way to do it.