0
votes

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?

2

2 Answers

5
votes

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) 
2
votes

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.