用于创建记录的Clojure宏

时间:2013-01-30 16:15:58

标签: macros clojure record

我写了一个用于创建记录的宏

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

我创建了一个实体

(def-entity p a b)

但是当我尝试创建一个具体的实例时     (def某事(p。“a”“b”)) 我收到此消息java.lang.IllegalArgumentException:找不到类user.p(repl-1:40)的匹配ctor。 所以我必须提供3个参数,像这样     (def某人(p。“a”“b”“x”)) 它把这样的值放在

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

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

2 个答案:

答案 0 :(得分: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) 

答案 1 :(得分: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)返回的内容。