0

我写了一个宏来创建记录

(defmacro def-entity [name & value]
 `(do
    (defrecord ~name (vector  ~@value))
    ))

我创建了一个实体

(def-entity p a b)

但是当我尝试创建一个具体实例(def something (p. "a" "b"))时,我收到这条消息 java.lang.IllegalArgumentException: No matching ctor found for class user.p (repl-1:40)。所以我必须提供3个参数,像这样(def某人(p。“a”“b”“x”))它把这样的值

 (-> someone :a)
 "b"
 (-> neko :p)
 nil

我似乎不明白发生了什么?

4

2 回答 2

5

由于defrecord它本身是一个宏,并且期望记录的字段作为向量字面量传入,因此您最好避免传递引用向量的符号,并真正构建一个向量字面量作为宏工作的一部分:

(defmacro defentity [name & values]
  `(defrecord ~name ~(vec values)))

结果是:

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"}

相反,您的版本会产生以下结果,它不使用向量文字作为输入defrecord

user=> (macroexpand-1 '(def-entity p a b))
(do (clojure.core/defrecord p (clojure.core/vector a b)))

事实上,我什至不能在 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) 
于 2013-01-30T16:51:29.750 回答
2

使用[~@value]代替(vector ~@value)

(defmacro def-entity [name & value]
 `(do
    (defrecord ~name [~@value])
    ))

实际上,您的变体对我根本不起作用。(def-entity p a b)抛出异常。但我可以建议(def-entity p a b)扩展(defrecord p (vector a b))并定义一个宏,并将它的第二个参数作为字段列表。第二个参数是(vector a b)- 它是一个包含 3 个元素的列表。因此它创建了具有 3 个字段的记录:vector、a 和 b。检查(:vector someone)返回的内容。

于 2013-01-30T16:19:32.727 回答