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?

share|improve this question
3  
"symbol-name, alter string, intern" sequence is the standard way. Please reference the posts which deprecate it. – sds May 3 '15 at 15:12
    
    
I don't see those answers deprecating symbol creation at macroexpand time. In fact, this is what defclass et al do! – sds May 3 '15 at 15:19
    
So something like this is OK: (defun append-symbol (symbol appstr) (intern (concatenate 'string (symbol-name symbol) appstr))), (defmacro build-stat-pane (name statslist) (let ((pane-symbols (loop for (n sname) in statslist collecting (list (append-symbol n "-title") (append-symbol n "-pane") sname)))) `(define-interface ,name () () (:panes ,@(loop for (ti p n) in pane-symbols collecting `(,ti title-pane :text ,n) collecting `(,p display-pane)))))) – Mark Green May 3 '15 at 15:36
    
yes, fine (hard to read in a comment though) – sds May 3 '15 at 15:47
up vote 2 down vote accepted

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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.