From: Dr. Edmund Weitz
Subject: removing a DEFSTRUCT structure
Date: 
Message-ID: <MPG.15d7ca419c1d9311989688@news.cis.dfn.de>
Hello!

The CLHS says that "the consequences of redefining a DEFSTRUCT structure 
are undefined."

OK, I understand. But I have a macro that, amongst other things, defines 
a DEFSTRUCT structure and I don't want to use GENSYM for the name of the 
structure in order to increase the readability of the code.

My first version used the same name in every call of the macro and 
although I encountered no problems with LispWorks or CMUCL, I felt that 
it wasn't really good not to conform to the standard. My second version 
uses a name provided by the caller of the macro but that's not really a 
solution because now the caller has to keep track if he hasn't used this 
name before.

As all of the structure's instances are local to functions, only the 
structure definition itself is left hanging around after the work is 
done. So, what I'm really looking for is a solution like 

1. somehow making the structure definition "local" to the macro

or, even better,

2. something like (when (exists-structure-definition name) (remove-
structure-definition name))

Is that possible? How would one do it?

Thanks,
Dr. Edmund Weitz
Hamburg
Germany

From: Kent M Pitman
Subject: Re: removing a DEFSTRUCT structure
Date: 
Message-ID: <sfwd769l3i5.fsf@world.std.com>
Dr. Edmund Weitz <···@agharta.de> writes:

> The CLHS says that "the consequences of redefining a DEFSTRUCT structure 
> are undefined."
> 
> OK, I understand. But I have a macro that, amongst other things, defines 
> a DEFSTRUCT structure and I don't want to use GENSYM for the name of the 
> structure in order to increase the readability of the code.

It depends on what features of DEFSTRUCT you are using as to how to
proceed.

For example, if you are not using the type predication feature but
only the data layout part, for example, then you could just use
DEFSTRUCT once and then GENSYM different aliases as accessors.

> My first version used the same name in every call of the macro and 
> although I encountered no problems with LispWorks or CMUCL, I felt that 
> it wasn't really good not to conform to the standard. My second version 
> uses a name provided by the caller of the macro but that's not really a 
> solution because now the caller has to keep track if he hasn't used this 
> name before.

It also depends on whether this happens at toplevel or embedded in a
form.  If you're embedded in a form, expanding to a DEFSTRUCT may be hard
since you may not get all the compilation effect you should.  You can't get
a local type.  You can only get a global type, and you can maybe if you're
clever get it uniquely named.  But you can mess this up.  Can you post
the definition?
 
> As all of the structure's instances are local to functions, only the 
> structure definition itself is left hanging around after the work is 
> done. So, what I'm really looking for is a solution like 
> 
> 1. somehow making the structure definition "local" to the macro
> 
> or, even better,
> 
> 2. something like (when (exists-structure-definition name) (remove-
> structure-definition name))
> 
> Is that possible? How would one do it?

Most things are possible, but some are more painful than others.
You're really pushing the point of pain here.

The conventional question to ask at this point is:

 What are you REALLY trying to do?

Asking an obscure technical question is often a symptom of an ill-formed
conceptual question, since usually at the higher level of the concept
there is a more straightforward answer.  The more you insist on doing the
abstract thing in a particular, obscure concrete way, the more you're likely
boxed into a corner.

Trivially, for example, you could replace your gensym-named structure with
a structure of fixed type with its own "subtype" tag (that is, an extra slot
that contained a marker used in a type test).  Suppose the type is FOO,
for example. You might make a FOO-P predicate which takes not only the
object but also the gensym and checks that the gensym is in the subtype
field of the struct before working with it.  Of course, this wouldn't 
do native dispatch under CLOS.  But you could do two-tiered dispatch
either through CLOS or more likely second level through one or more
global hash tables, keyed on the gensym'd token.  A lot depends on what
your'e trying to achieve and what your constraints are.
From: Dr. Edmund Weitz
Subject: Re: removing a DEFSTRUCT structure
Date: 
Message-ID: <MPG.15da0624b47ca7a7989689@news.cis.dfn.de>
In article <···············@world.std.com>, ······@world.std.com says...

> It depends on what features of DEFSTRUCT you are using as to how to
> proceed.
> 
> For example, if you are not using the type predication feature but
> only the data layout part, for example, then you could just use
> DEFSTRUCT once and then GENSYM different aliases as accessors.

If "type predication" means using features like (DECLARE (TYPE ...)), 
then I don't use it. But the structure itself, namely the number of 
slots, may change from invocation to invocation.

> It also depends on whether this happens at toplevel or embedded in a
> form.  If you're embedded in a form, expanding to a DEFSTRUCT may be hard
> since you may not get all the compilation effect you should.  You can't get
> a local type.  You can only get a global type, and you can maybe if you're
> clever get it uniquely named.  But you can mess this up.  Can you post
> the definition?

It happens inside of a form. What are the compilation features that I 
might miss here?

The code you're asking for can be found at
	http://www.weitz.de/einstein.html
	http://www.weitz.de/files/riddle.lisp
	http://www.weitz.de/files/einstein.lisp

I won't argue with you whether the DEFSTRUCT is really necessary here - 
of course it isn't. In fact, I just use it as a simple-vector and I 
might as well change the code to get rid of my problem (that I posted 
here) at all. I'm mainly using DEFSTRUCT here because I want the code 
generated by the PREPARE macro to look nice.

But apart from my particular code I can imagine situations where it 
might be useful to have the kind of control that I asked for. Suppose 
you have a similar macro that accepts some kind of problem definition 
from a user and then prepares some code and data structures for him, 
afterwards putting him back into the read-eval-print-loop where he can 
use this code and structures to work on his problem. The data structures 
might include DEFSTRUCTs the structure of which could depend on the 
particular problem. And it would be a lot easier for the user if he 
could work with something like
	(setq goofy (make-dog))
	(setf (dog-breed goofy) 'poodle)
instead of
	(setq goofy (make-g456))
	(setf (g456-g577 goofy) 'poodle)
or whatever it might look like.

I'm still wondering how I could force my Lisp system to completely 
"forget" the whole DOG definition including the accessor functions - 
just in case the user might want to work an another problem that 
accidently also involves pets.

Thanks for your help,
Edi Weitz.
From: Kent M Pitman
Subject: Re: removing a DEFSTRUCT structure
Date: 
Message-ID: <sfwu1zj9a49.fsf@world.std.com>
Dr. Edmund Weitz <···@agharta.de> writes:

> If "type predication" means using features like (DECLARE (TYPE ...)), 
> then I don't use it. But the structure itself, namely the number of 
> slots, may change from invocation to invocation.

Then you can do something like

 (defvar *n-word-foo-structures* '())

 (defun get-n-word-foo-structure (n)
   (check-type n (integer 0) "a whole number")
   (or (gethash n *n-word-foo-structures*)
       (setf (gethash n *n-word-foo-structures*)
             (let ((name (intern (format nil "FOO~D" n)))
		   (slot (intern (format nil "FOO-SLOT-~D" n)))
		   (name-1 (intern (format nil "FOO~D" (- n 1)))))
               (eval `(defstruct (,name (:conc-name nil)
				     ,@(unless (= n 0)
					 `((:include ,name-1))))
			,slot))
               name))))

I didn't test this.  But hopefully it's close enough to give you an idea.
It might need an (eval-when (:execute :load-toplevel :compile-toplevel) ..)
around those two forms, but I didn't test that either.
 
> > It also depends on whether this happens at toplevel or embedded in a
> > form.  If you're embedded in a form, expanding to a DEFSTRUCT may be hard
> > since you may not get all the compilation effect you should.  You can't get
> > a local type.  You can only get a global type, and you can maybe if you're
> > clever get it uniquely named.  But you can mess this up.  Can you post
> > the definition?
> 
> It happens inside of a form. What are the compilation features that I 
> might miss here?

Well, the compiler isn't allowed to prescan it and assume it applies to
subsequent things since they might not be in the execution path.  You might
assume it applies at least within the local lexical contour, but I doubt
the compiler would since (due to "halting problem") it can't always win.
So it will generate definitions at execute time and will treat your accessors
as closed-coded function calls, not open-coding (inlining) them like you
might expect.
 
> The code you're asking for can be found at
> 	http://www.weitz.de/einstein.html
> 	http://www.weitz.de/files/riddle.lisp
> 	http://www.weitz.de/files/einstein.lisp

Thanks for the pointer but I'm in a hurry so didn't look.
 
> I won't argue with you whether the DEFSTRUCT is really necessary here - 
> of course it isn't. In fact, I just use it as a simple-vector and I 
> might as well change the code to get rid of my problem (that I posted 
> here) at all. I'm mainly using DEFSTRUCT here because I want the code 
> generated by the PREPARE macro to look nice.

Nothing keeps you from having a vector with a set of slot accessors.
You could even generate them with a local binding form.

 (defmacro with-vector-accessors (prefix part-list)
   (let ((maker (intern (format nil "MAKE-~A" prefix)))
         (accessors (loop for part in part-list 
			  for i from 0
                          for name = (intern (format nil "~A-~A" prefix part))
			  collect `(,name (,prefix) (svref ,prefix ,i)))))
   `(flet ((,maker () (make-array ,(length part-list)))
	   ,@accessors)
      ...)))

Then in your code you'd write:

  (with-vector-accessors (frob (wings tail arms))
    ... use make-frob, frob-wings, frob-tail, frob-arms...)

and it would just be doing make-array and svref.
 
> But apart from my particular code I can imagine situations where it 
> might be useful to have the kind of control that I asked for. Suppose 
> you have a similar macro that accepts some kind of problem definition 
> from a user and then prepares some code and data structures for him, 
> afterwards putting him back into the read-eval-print-loop where he can 
> use this code and structures to work on his problem. The data structures 
> might include DEFSTRUCTs the structure of which could depend on the 
> particular problem. And it would be a lot easier for the user if he 
> could work with something like
> 	(setq goofy (make-dog))
> 	(setf (dog-breed goofy) 'poodle)
> instead of
> 	(setq goofy (make-g456))
> 	(setf (g456-g577 goofy) 'poodle)
> or whatever it might look like.

Sure.  One of the above examples should work for you.

> I'm still wondering how I could force my Lisp system to completely 
> "forget" the whole DOG definition including the accessor functions - 
> just in case the user might want to work an another problem that 
> accidently also involves pets.

The easiest way is to give him different packages each time, and make
sure all your INTERN's are done relative to his user package.  Then
you can DELETE-PACKAGE to start over (or just make a new package,
keeping both environments available in case he wants to go back) or
you can edit out certain symbols with UNINTERN without it potentially
affecting your own programs.

> Thanks for your help,
> Edi Weitz.

Good luck.
From: Dr. Edmund Weitz
Subject: Re: removing a DEFSTRUCT structure
Date: 
Message-ID: <MPG.15dbd0cdf54e672498968a@news.cis.dfn.de>
In article <···············@world.std.com>, ······@world.std.com says...

> Then you can do something like
> ...

Thanks for your examples, I will check them and try to learn from them.

> Well, the compiler isn't allowed to prescan it and assume it applies to
> subsequent things since they might not be in the execution path.  You might
> assume it applies at least within the local lexical contour, but I doubt
> the compiler would since (due to "halting problem") it can't always win.
> So it will generate definitions at execute time and will treat your accessors
> as closed-coded function calls, not open-coding (inlining) them like you
> might expect.

Hmmm, I always considered it a very appealing feature of Lisp to be able 
to generate code at execution time. Would it help if I my macro 
generated something like the code below?

   (defun foo (...
   (compile 'foo)

I mean: Would the code actually get recompiled at macro expansion time - 
giving the compiler a chance to optimize it by inlining code it didn't 
know about before?

> The easiest way is to give him different packages each time, and make
> sure all your INTERN's are done relative to his user package.  Then
> you can DELETE-PACKAGE to start over (or just make a new package,
> keeping both environments available in case he wants to go back) or
> you can edit out certain symbols with UNINTERN without it potentially
> affecting your own programs.

Thanks for this hint. I didn't even think about packages yet. (There's a 
lot more stuff to learn...)

Thanks again,
Edi.
From: Kent M Pitman
Subject: Re: removing a DEFSTRUCT structure
Date: 
Message-ID: <sfwelql6ftg.fsf@world.std.com>
Dr. Edmund Weitz <···@agharta.de> writes:

> Would it help if I my macro 
> generated something like the code below?
> 
>    (defun foo (...
>    (compile 'foo)

No, that might work in some implementations but it's not supposed to.
Technically, those compilation environments are separate and you're not
supposed to rely on them to be the same.

but you're missing the point.  Code emitted at compile-time is either
in-line to be compiled already or is occurring at a place where execution
is deferred. e.g., in compiling a
 (foo)
which expands into
 (progn (defstruct foo ) ... use foo struct ...)
this does compile foo and optimize it at compile time.
However, if you talk about
 (lambda () (foo) ...use foo ...)
you're talking about something that can't rely on the function ever getting
called.  if it WERE called, it would affect all points in your program,
even outside the lambda because defstruct is not scoped to the lambda.
But if it were not called, the macros can't know.
And the problem is worse because
 (lambda (x) (if x (foo)) ... use foo struct...)
might be trying to use either some old foo definition (if the x is false)
or some new definition (if the x  is true). so again, though it seems
like
 (lambda () (foo) ...use foo...)
is "obvious", it's trivial to make the second form conditional and then
make it less obvious.  Rather than make the user of the language know
just how bright the compiler is (when it can and cannot prove that something
will or won't get executed, and when it can and can't avoid the halting
problem), we just tell people a simpler and more reliable rule:

definitional forms expand into two parts:
 (a) an execution part--something that establishes the definitions.
 (b) an optimization part--something used by the compiler at toplevel
     if the form occurs that way and not used otherwise.
the former usually comes out of the expansion in an
 (eval-when (:execute) ...)
and the latter in
 (eval-when (:compile-toplevel) ...)
and/or
 (eval-when (:load-toplevel) ...)
though there are various ways to manage it.  The semantics of eval-when
is such that the :compile-toplevel and :load-toplevel things just get
ignored when you place the form "not at toplevel" (such as a point
of deferred execution).

> I mean: Would the code actually get recompiled at macro expansion time - 
> giving the compiler a chance to optimize it by inlining code it didn't 
> know about before?

You really should go into CLHS and spend some time reading about the real
semantics of the language on these points.  It goes into some detail
about macro expansion, evaluation, compilation, minimal compilation,
evaluation times, and all that.
 
> > The easiest way is to give him different packages each time, and make
> > sure all your INTERN's are done relative to his user package.  Then
> > you can DELETE-PACKAGE to start over (or just make a new package,
> > keeping both environments available in case he wants to go back) or
> > you can edit out certain symbols with UNINTERN without it potentially
> > affecting your own programs.
> 
> Thanks for this hint. I didn't even think about packages yet. (There's a 
> lot more stuff to learn...)
> 
> Thanks again,
> Edi.
> 
Ok. Good luck. I'm sure you'll have more questions when you get that far.