简体   繁体   English

在宏上使用循环在Common Lisp中生成类槽

[英]Using loop on a macro to generate classes slots in Common Lisp

While making classes on CLOS I've met the same pattern multiple times: 在CLOS上创建类时,我多次遇到相同的模式:

(defclass class-name () 
  ((field-1
      :initarg field-1
      :initform some-value
      :accessor field-1)
   (field-2
      :initarg field-2
      :initform another-value
      :accessor field-2)
   (...)
   (field-n
      :initarg field-n
      :initform n-value
      :accessor field-n)))

(whether this is good design is something I'll learn with time) (这是否是好的设计是我将随着时间学习的东西)

I've tried to tackle this with a macro so I could call, say: 我试图用宏来解决这个问题所以我可以打电话,说:

(defclass-with-accessors 'class-name
   (('field-1 some-value)
    ('field-2 another-value)
    (...)
    ('field-n n-value)))

My first tackle (ignoring hygiene for now) was to split into two macros: one to make each field, other to make the class itself. 我的第一个解决方法(暂时忽略卫生)是分成两个宏:一个用于制作每个字段,另一个用于制作类本身。

The macro to make the accessor fields seems to be correct: 使访问者字段的宏​​似乎是正确的:

(defmacro make-accessor-field (name form)
  `(,name
   :initarg ,(make-keyword name)
   :initform ,form
   :accessor ,name))

But I'm not getting the main macro right. 但我没有得到主要的宏观权利。 My first attempt was: 我的第一次尝试是:

(defmacro defclass-with-accessors (name body)
  `(defclass ,name () \(
     ,(loop for my-slot in body collect
       (make-accessor-field (car my-slot) (cadr my-slot)))))

But this isn't valid, SBCL giving me the following error at the defmacro evaluation: 但这是无效的,SBCL在defmacro评估中给出了以下错误:

; in: DEFMACRO DEFCLASS-WITH-ACCESSORS
;     (MAKE-ACCESSOR-FIELD (CAR MY-SLOT) (CADR MY-SLOT))
; 
; caught ERROR:
;   during macroexpansion of (MAKE-ACCESSOR-FIELD (CAR MY-SLOT) (CADR MY-SLOT)).
;   Use *BREAK-ON-SIGNALS* to intercept.
;   
;    The value (CAR MY-SLOT)
;    is not of type
;      (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER).
; 
; compilation unit finished
;   caught 1 ERROR condition
STYLE-WARNING:
   redefining COMMON-LISP-USER::DEFCLASS-WITH-ACCESSORS in DEFMACRO

What's happening exactly? 到底发生了什么? How can the compiler tell the type of (car slot) when slot isn't even defined? 当插槽甚至没有定义时,编译器如何告诉(汽车插槽)的类型? How can I proceed to correctly define this macro? 如何继续正确定义此宏?

The basic mistakes 基本的错误

This macro is wrong, because it should not be a macro: 这个宏是错误的,因为它不应该是一个宏:

(defmacro make-accessor-field (name form)
  `(,name
   :initarg ,(make-keyword name)
   :initform ,form
   :accessor ,name))

Macro forms should expand into code. 宏表单应扩展为代码。 This macro expands a form into a list used for a slot description. 此宏将表单扩展为用于插槽描述的列表。 A slot description is not code, but a part of the defclass slots list. 插槽描述不是代码,而是defclass插槽列表的一部分。 Thus you can't use a macro like this, because the value returned should be code, not a slot description list. 因此,您不能使用这样的宏,因为返回的值应该是代码,而不是插槽描述列表。

Also one usually would not use MAKE- in a macro name. 通常也不会在宏名称中使用MAKE- That's more a convention. 这更像是一种惯例。 MAKE-SOMETHING should be a function. MAKE-SOMETHING应该是一个功能。 Whenever you make something, there is something being created at runtime and thus it should be a function. 每当你的东西,有在运行时创建的东西,因此它应该是一个函数。 Sometimes one also wants to apply make to a list of things and then again, a function is preferred. 有时候,人们也想将make应用于事物列表,然后再次使用函数。

This also is wrong, because there is a symbol with a parenthesis as its name: 这也是错误的,因为有一个带括号的符号作为其名称:

(defmacro defclass-with-accessors (name body)
  `(defclass ,name () \(    ;  <-  what is this?
     ,(loop for my-slot in body collect
       (make-accessor-field (car my-slot) (cadr my-slot)))))

This code is also not a good idea, because the quotes are not useful: 这段代码也不是一个好主意,因为引号没用:

(defclass-with-accessors 'class-name
   (('field-1 some-value)
    ('field-2 another-value)
    (...)
    ('field-n n-value)))

If you look at defclass , it does not need quoted names. 如果你看一下defclass ,它不需要带引号的名字。 Thus in your defclass variant, there should be no quotes either. 因此,在你的defclass变体中,也应该没有引号。

Let's try to improve it 让我们尝试改进它

Example form from some imaginary code base: 一些虚构代码库的示例表单

(defclass-with-accessors foo
   ((bar 10)
    (baz (sin pi)))

MAKE-ACCESSOR-FIELD is now a function: MAKE-ACCESSOR-FIELD现在是一个功能:

(defun make-accessor-field (name form)
  `(,name
    :initarg  ,(intern (symbol-name name) "KEYWORD")
    :initform ,form
    :accessor ,name))

The new DEFCLASS-WITH-ACCESSORS : 新的DEFCLASS-WITH-ACCESSORS

(defmacro defclass-with-accessors (name slot-descriptions)
  `(defclass ,name ()
     ,(loop for (slot-name form) in slot-descriptions
            collect (make-accessor-field slot-name form))))

Let's check the expansion: 我们来看看扩展:

macroexpand-1 expands a form once at the toplevel and pprint prints the s-expression in some automagically formatted way: macroexpand-1在顶层扩展一次表单, pprint以一些自动格式化的方式打印s表达式:

CL-USER 12 > (pprint (macroexpand-1 '(defclass-with-accessors foo
                                         ((bar 10)
                                          (baz (sin pi))))))

(DEFCLASS FOO
          NIL
          ((BAR :INITARG :BAR :INITFORM 10 :ACCESSOR BAR)
           (BAZ :INITARG :BAZ :INITFORM (SIN PI) :ACCESSOR BAZ)))

Looks okay. 看起来没问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM