From: Andrew Cooke
Subject: Macros and interning variables
Date: 
Message-ID: <80hetq$tgj$1@nnrp1.deja.com>
Hi,

As I can't get a list of struct slots (see earlier thread + faq) I
thought I would solve my problems by using a macro.  Instead of having
a generic print-function for my structs (one that would need to know
the list of slots), my macro would generate a print function "on the
fly".

Unfortunately, this doesn't work because the print function is within
the body of the defstruct, and until the defstruct is completed,
accessors are not generated.  So my macro expands to something like (I
can't cut + paste as I am working on another machine, but this is the
general idea):

> (macroexpand-1 (my-defstruct ......))
(defstruct (zx-char (:include .....)
                    (:print-function
                      (lambda (instance stream depth)
                        (format stream "contains character ~a"
                          (#:zx-char-char instance))))
             (char)))

Here zx-char contains a char slot, which will be accessed by
zx-char-char.  But note the unfortunate #: before the accessor.

I am generating the symbols like zx-char-char in my macro using this
function:

(defun new-symbol (prefix suffix &optional (exists nil))
  (let ((new-sym (concatenate 'string (string prefix) (string suffix))))
    (when exists
      (unless (find-symbol new-sym)
        (intern new-sym)))
    (make-symbol new-sym)))

But I get the problem above even when I call this with exists set to t
- in other words, even though the symbol is being interned, the macro
is being expanded with that annoying #:.

What am I doing wrong?  It's as though I need a kind of defstruct*
that lets me refer to the struct while building it.  I realise there
may be an easier way to solve the high-level problem (although I am
enjoying playing with macros - the whole backquote and comma notation
is the most elegant solution to the problem of generating code that I
have met), but I can see this happening in other contexts too - is
there no way to have a macro refer to symbols that do not yet exist?

I've been to the bookshop and Graham's "On Lisp" that I ordered 3
weeks ago is still not in - apparently the publishers are out of
stock.  Also, I am decorating the house and may have to unplug
everything soon, so please forgive a lack of response if you reply - I
will read this on Dejanews as soon as I get connected again...

Cheers,
Andrew



Sent via Deja.com http://www.deja.com/
Before you buy.

From: Samir Barjoud
Subject: Re: Macros and interning variables
Date: 
Message-ID: <wkbt8z4or8.fsf@mindspring.com>
Andrew Cooke <······@andrewcooke.free-online.co.uk> writes:

> Hi,
> 
> As I can't get a list of struct slots (see earlier thread + faq) I
> thought I would solve my problems by using a macro.  Instead of having
> a generic print-function for my structs (one that would need to know
> the list of slots), my macro would generate a print function "on the
> fly".

You can have a macro wrapper around DEFSTRUCT expand into a call to
DEFSTRUCT and to a PRINT-OBJECT method definition.  The method is
hardcoded to print the slots that are known from the
`slot-descriptions' argument of the macro.

...................
(defun car-or-atom (x)
  (if (atom x) 
      x
    (car x)))

(defun mkstr (&rest objects)
  (with-output-to-string (*standard-output*)
    (mapc #'princ objects)))

(defun symb (&rest objects)
  (intern (apply #'mkstr objects)))

(defmacro my-defstruct (name-and-options &rest slot-descriptions)
  `(progn
     (defstruct ,name-and-options ,@slot-descriptions)
     ,(let ((struct-name (car-or-atom name-and-options)))
	`(defmethod print-object ((object ,struct-name) stream)
	   (format stream ,(mkstr "A " struct-name " with the following slots and values:~%"))
	   ,@(loop with conc-name = (car-or-atom name-and-options)
		 for slot in slot-descriptions
		 for accessor = (symb conc-name "-" slot)
		 collect `(format stream ,(mkstr slot " = ~A~%")
				  (,accessor object)))))))
...................

-- 
Samir Barjoud
·····@mindspring.com
From: Andrew Cooke
Subject: Sorry - fixed [Re: Macros and interning variables]
Date: 
Message-ID: <80hlbq$2ps$1@nnrp1.deja.com>
Sorry, my mistake.  Works if I don't call make-symbol after interning
(duh!).

Andrew

In article <············@nnrp1.deja.com>,
  Andrew Cooke <······@andrewcooke.free-online.co.uk> wrote:
> > (macroexpand-1 (my-defstruct ......))
> (defstruct (zx-char (:include .....)
>                     (:print-function
>                       (lambda (instance stream depth)
>                         (format stream "contains character ~a"
>                           (#:zx-char-char instance))))
>              (char)))
[...]
> (defun new-symbol (prefix suffix &optional (exists nil))
>   (let ((new-sym (concatenate 'string (string prefix) (string
suffix))))
>     (when exists
>       (unless (find-symbol new-sym)
>         (intern new-sym)))
>     (make-symbol new-sym)))


Sent via Deja.com http://www.deja.com/
Before you buy.
From: Barry Margolin
Subject: Re: Sorry - fixed [Re: Macros and interning variables]
Date: 
Message-ID: <RuZW3.27$KP4.837@burlma1-snr2>
In article <············@nnrp1.deja.com>,
Andrew Cooke  <······@andrewcooke.free-online.co.uk> wrote:
>
>
>Sorry, my mistake.  Works if I don't call make-symbol after interning
>(duh!).

Presumably you meant to use IF rather than WHEN, and have the MAKE-SYMBOL
in the else-clause.

Also, there's a problem with your (unless (find-symbol ...)...) code: if
the symbol is found, NEW-SYMBOL won't return it!  You should just call
INTERN; you don't need to test with FIND-SYMBOL first, because INTERN will
always return an existing symbol if it's already interned.  Effectively,
INTERN is defined as somthing like:

(defun intern (name &optional (package *package*))
  (let ((existing (find-symbol name package)))
    (if existing
        existing
        (let ((new-symbol (make-symbol name)))
          (system-internal::add-to-package new-symbol package)
          (setf (symbol-package new-symbol) package)
          new-symbol))))

>Andrew
>
>In article <············@nnrp1.deja.com>,
>  Andrew Cooke <······@andrewcooke.free-online.co.uk> wrote:
>> > (macroexpand-1 (my-defstruct ......))
>> (defstruct (zx-char (:include .....)
>>                     (:print-function
>>                       (lambda (instance stream depth)
>>                         (format stream "contains character ~a"
>>                           (#:zx-char-char instance))))
>>              (char)))
>[...]
>> (defun new-symbol (prefix suffix &optional (exists nil))
>>   (let ((new-sym (concatenate 'string (string prefix) (string
>suffix))))
>>     (when exists
>>       (unless (find-symbol new-sym)
>>         (intern new-sym)))
>>     (make-symbol new-sym)))
>
>
>Sent via Deja.com http://www.deja.com/
>Before you buy.


-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.