基于协议动态定义记录的宏

时间:2017-04-12 15:06:41

标签: clojure

我的目标是创建一个宏,(defdummy MyProtocol MyImplementation)将创建一个记录类型defrecord。仅生成MyProtocol reify的实现也是可以接受的。此记录类型应实现给定协议的所有方法,但只为每个方法返回nil。 e.g:

(defprotocol Annoying
  (beep [x] "Make a sound"))

;; This form...    
(defdummy Annoying FakeAnnoyer)

;; Should expand to this
(defrecord FakeAnnoyer []
  Annoying
  (beep [x] nil))

我看到当我们评估协议时,我们有一些方法可用于生成协议方法的实现::sigs:arglists等。但是,因为宏接收它们的参数没有评估,我无法从宏内部访问此信息。如果宏用户将协议MyProtocol传递给我的宏,我只会看到符号'MyProtocol。在我看来,这些信息在编译时是静态知道的,但我不知道在生成协议方法时是否可以找到它。

我能想到的唯一方法是使用eval或扩展到defrecord的某些内部工作方式。还有其他办法吗?

1 个答案:

答案 0 :(得分:3)

您可以使用resolve功能。例如,以下是defdummy宏的基本实现:

(defmacro defdummy [protocol record]
  `(defrecord ~record []
     ~protocol
     ~@(for [[_ {:keys [name arglists]}] (:sigs @(resolve protocol))]
         `(~name ~@arglists nil))))

示例:

(defprotocol Annoying
  (beep [x] "Make a sound"))

(macroexpand-1 '(defdummy Annoying FakeAnnoyer))
;;=> (clojure.core/defrecord FakeAnnoyer [] Annoying (beep [x] nil))