Mark Green
Mark Green

Reputation: 1330

In Common Lisp, what's the best way to write a macro that defines a class?

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:

(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?

Upvotes: 2

Views: 255

Answers (1)

Rainer Joswig
Rainer Joswig

Reputation: 139411

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:

(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

Upvotes: 2

Related Questions