在Common Lisp中,编写定义类的宏的最佳方法是什么?

时间:2022-02-26 22:37:08

I'm trying to write a macro in Common Lisp that defines a class with variant slots I specify. So far it's worked OK (and I've been very impressed with clisp!) for this:

我正在尝试在Common Lisp中编写一个宏,它定义了一个我指定的变量槽的类。到目前为止它的工作正常(我对clisp印象非常深刻!)为此:

(defmacro notifier (class slot) 
  "Defines a setf method in (class) for (slot) which calls the object's changed method."
   `(defmethod (setf ,slot) (val (item ,class))
     (setf (slot-value item ',slot) val)
     (changed item ',slot)))

(defmacro notifiers (class slots)
  "Defines setf methods in (class) for all of (slots) which call the object's changed method."
  `(progn 
     ,@(loop for s in slots collecting `(notifier ,class ,s))))

(defmacro defclass-notifier-slots (class nslots slots)
  "Defines a class with (nslots) giving a list of slots created with notifiers, and (slots) giving a list of slots created with regular accessors."
  `(progn
     (defclass ,class () 
       ( ,@(loop for s in nslots collecting `(,s :reader ,s)) 
         ,@(loop for s in slots collecting `(,s :accessor ,s))))
     (notifiers ,class ,nslots)))

The problem is, now I want to create not just the slots I specify in the call to the macro, but some other slots as well with variant names. In order to do that, I'd have to use an ugly "symbol-name, alter string, intern" sequence to generate the variant name as a slot name, and I've already seen answers on SO that say you should avoid doing that. So is there a better way of doing this?

问题是,现在我想创建的不仅是我在宏调用中指定的插槽,还有一些其他插槽以及变体名称。为了做到这一点,我必须使用一个丑陋的“符号名称,改变字符串,实习生”序列来生成变体名称作为插槽名称,我已经看到了SO的答案,说你应该避免做那。那么有更好的方法吗?

1 个解决方案

#1


There is nothing wrong with constructing new symbols inside a macro or one of its helper functions.

在宏或其辅助函数之一中构造新符号没有任何问题。

Whenever you need something multiple times or you need to document it in some way, write a function.

每当您需要多次或需要以某种方式记录它时,请编写一个函数。

Since we need to use a possibly new symbol, it makes sense to make sure the symbol is in the right package. Here we just assume the package of the prefix symbol is the right package.

由于我们需要使用一个可能是新的符号,因此确保符号位于正确的包中是有意义的。这里我们假设前缀符号的包是正确的包。

(defun make-suffix-symbol (prefix suffix)
  (check-type prefix symbol)
  (check-type suffix (or string symbol))
  (when (symbolp suffix)
    (setf suffix (symbol-name suffix)))
  (intern (concatenate 'string
                       (symbol-name prefix)
                       suffix)
          (symbol-package prefix)))

CL-USER 12 > (make-suffix-symbol 'http-user::foo "BAR")
HTTP-USER::FOOBAR

or using FORMAT:

或使用FORMAT:

(defun make-suffix-symbol (prefix suffix &optional (format-string "~A~A"))
   (check-type prefix symbol)
   (check-type suffix (or string symbol))
   (when (symbolp suffix)
     (setf suffix (symbol-name suffix)))
   (intern (format nil
                   format-string
                   (symbol-name prefix)
                   suffix)
           (symbol-package prefix)))

CL-USER 14 > (make-suffix-symbol 'http-user::foo "BAR" "~a-~a")
HTTP-USER::FOO-BAR

#1


There is nothing wrong with constructing new symbols inside a macro or one of its helper functions.

在宏或其辅助函数之一中构造新符号没有任何问题。

Whenever you need something multiple times or you need to document it in some way, write a function.

每当您需要多次或需要以某种方式记录它时,请编写一个函数。

Since we need to use a possibly new symbol, it makes sense to make sure the symbol is in the right package. Here we just assume the package of the prefix symbol is the right package.

由于我们需要使用一个可能是新的符号,因此确保符号位于正确的包中是有意义的。这里我们假设前缀符号的包是正确的包。

(defun make-suffix-symbol (prefix suffix)
  (check-type prefix symbol)
  (check-type suffix (or string symbol))
  (when (symbolp suffix)
    (setf suffix (symbol-name suffix)))
  (intern (concatenate 'string
                       (symbol-name prefix)
                       suffix)
          (symbol-package prefix)))

CL-USER 12 > (make-suffix-symbol 'http-user::foo "BAR")
HTTP-USER::FOOBAR

or using FORMAT:

或使用FORMAT:

(defun make-suffix-symbol (prefix suffix &optional (format-string "~A~A"))
   (check-type prefix symbol)
   (check-type suffix (or string symbol))
   (when (symbolp suffix)
     (setf suffix (symbol-name suffix)))
   (intern (format nil
                   format-string
                   (symbol-name prefix)
                   suffix)
           (symbol-package prefix)))

CL-USER 14 > (make-suffix-symbol 'http-user::foo "BAR" "~a-~a")
HTTP-USER::FOO-BAR