Reputation: 345
I wrote a macro for creating a record
(defmacro def-entity [name & value]
`(do
(defrecord ~name (vector ~@value))
))
and I created one entity
(def-entity p a b)
But when I try to create a concrete instance (def something (p. "a" "b")) I get this message java.lang.IllegalArgumentException: No matching ctor found for class user.p (repl-1:40). So I have to provide 3 parameters, like this (def someone (p. "a" "b" "x")) And it puts the values like this
(-> someone :a)
"b"
(-> neko :p)
nil
I don't seem to understand what is happening?
Upvotes: 0
Views: 622
Reputation: 13514
As defrecord
is itself a macro, and expects the fields of the record to be passed in as a vector literal, you should better avoid passing a symbol referring to a vector, and really construct a vector literal as part of your macro work:
(defmacro defentity [name & values]
`(defrecord ~name ~(vec values)))
Which results in:
user=> (macroexpand-1 '(defentity p a b))
(clojure.core/defrecord p [a b])
user=> (defentity Test foo bar)
user.Test
user=> (def s (Test. "foo" "bar"))
#'user/s
user=> s
#user.Test{:foo "foo", :bar "bar"}
In contrast, your version results in the following, which doesn't use a vector literal as input to defrecord
:
user=> (macroexpand-1 '(def-entity p a b))
(do (clojure.core/defrecord p (clojure.core/vector a b)))
In facts, I can't even use your version in Clojure 1.4:
user=> (def-entity w x y)
CompilerException java.lang.RuntimeException: Can't use qualified name as parameter: clojure.core/vector, compiling:(NO_SOURCE_PATH:1)
Upvotes: 5
Reputation: 15434
Use [~@value]
instead of (vector ~@value)
:
(defmacro def-entity [name & value]
`(do
(defrecord ~name [~@value])
))
Actually your variant doesn't work for me at all. (def-entity p a b)
throws an exception. But I can suggest that (def-entity p a b)
expands to (defrecord p (vector a b))
and defrecord itself a macro and it takes it's second argument as list of fields. Second argument is (vector a b)
- it's a list that contains 3 elements. So it creates record that has 3 fields: vector, a and b. Check what (:vector someone)
returns.
Upvotes: 2