From: Jonathan Braud on
Hello everyone,

I'm trying to write a version of defstruct that shadows the name of the
structure and exports the constructor and the slot accessors. The macro
I wrote macroexpands into what I expect it to, for instance

* (macroexpand-1 '(defstruct-shadow-export lambda slot))

(PROGN
(SHADOW 'LAMBDA)
(DEFSTRUCT LAMBDA SLOT)
(EXPORT 'MAKE-LAMBDA)
(EXPORT 'LAMBDA-SLOT))

but during the evaluation, I get an error saying: "[The] lock on package
COMMON-LISP [was] violated when defining LAMBDA as a structure".

If after getting the error, I try to define the structure again, it
works as expected. It's as if the shadowing actually occured after the
evaluation of the progn.

If any of you has an idea why this happens, and/or knows a way around
the problem...

Thanks in advance.
From: Tim Bradshaw on
On 2010-05-03 22:48:19 +0100, Jonathan Braud said:
>
> (PROGN
> (SHADOW 'LAMBDA)
> (DEFSTRUCT LAMBDA SLOT)
> (EXPORT 'MAKE-LAMBDA)
> (EXPORT 'LAMBDA-SLOT))
>

Think about read time compared with evaluation time

From: Pascal J. Bourguignon on
Jonathan Braud <jonathan.braud.NOSPAMTHANKYOUVERYMUCH+usenet(a)gmail.com>
writes:

> Hello everyone,
>
> I'm trying to write a version of defstruct that shadows the name of the
> structure and exports the constructor and the slot accessors. The macro
> I wrote macroexpands into what I expect it to, for instance
>
> * (macroexpand-1 '(defstruct-shadow-export lambda slot))
>
> (PROGN
> (SHADOW 'LAMBDA)
> (DEFSTRUCT LAMBDA SLOT)
> (EXPORT 'MAKE-LAMBDA)
> (EXPORT 'LAMBDA-SLOT))
>
> but during the evaluation, I get an error saying: "[The] lock on package
> COMMON-LISP [was] violated when defining LAMBDA as a structure".
>
> If after getting the error, I try to define the structure again, it
> works as expected. It's as if the shadowing actually occured after the
> evaluation of the progn.
>
> If any of you has an idea why this happens, and/or knows a way around
> the problem...

As Tim said, think about when things occur, read-time vs. run-time.

What do you think about the two occurences of the symbol LAMBDA in this
form?

The way around it is obvious, if you can read the above question.

--
__Pascal Bourguignon__
From: Thomas A. Russ on
Jonathan Braud <jonathan.braud.NOSPAMTHANKYOUVERYMUCH+usenet(a)gmail.com> writes:

> Hello everyone,
>
> I'm trying to write a version of defstruct that shadows the name of the
> structure and exports the constructor and the slot accessors. The macro
> I wrote macroexpands into what I expect it to, for instance
>
> * (macroexpand-1 '(defstruct-shadow-export lambda slot))
>
> (PROGN
> (SHADOW 'LAMBDA)
> (DEFSTRUCT LAMBDA SLOT)
> (EXPORT 'MAKE-LAMBDA)
> (EXPORT 'LAMBDA-SLOT))
>
> but during the evaluation, I get an error saying: "[The] lock on package
> COMMON-LISP [was] violated when defining LAMBDA as a structure".
>
> If after getting the error, I try to define the structure again, it
> works as expected. It's as if the shadowing actually occured after the
> evaluation of the progn.
>
> If any of you has an idea why this happens, and/or knows a way around
> the problem...

I'll try to be less cryptic than Pascal and Tim. :-)

The key, as they hint is that there is an issue with read-time verus
evaluation time. When the reader encounters the form

(macroexpand-1 '(defstruct-shadow-export lambda slot))

the first time, it reads the string "lambda" and interns it, which
results in getting the symbol CL:LAMBDA back. This is the symbol that
is then passed to your macro, and which is used in the expansion.

One way to kind of see what is happening is to do something like

(let ((*package* (find-package :keyword)))
(print (macroexpand-1 '(defstruct-shadow-export lambda slot))))

I arbitrarily chose the KEYWORD package to essentially force the printer
to print all of the symbols with their full package qualification. So
this will let you see exactly which symbols you are dealing with.

Essentially what is happening is that lisp has already read the string
"lambda" and turned it into CL:LAMBDA before the macro gets a chance to
do anything. It is only after the first execution of the code returned
by your macro expansion that "LAMBDA" is shadowed in your own code

So the second time you try the expansion, when the reader see "lambda"
and interns it, it gets a different symbol, namely CL-USER::LAMBDA (or
whatever package you are experimenting in instead of CL-USER).

Your problem, then, is to figure out how to get the symbol you want to
shadow to be shadowed at read time rather than at macro-expansion time.

It may be sufficient to have the macro expansion function also shadow
the symbol and do the substitution during macro-expansion. I don't have
the time to figure out for sure if this would work, but I think it is a
promising alternative to trying to do something using the reader. You
will have to make sure, though, that you shadow the symbol and then
substitute the symbol from the correct package for the name of the
defstruct.

Assuming that will work, you can try something like
(defmacro defstruct-shadow-export (name &rest slots)
...
(shadow name)
(let ((new-name (intern (symbol-name name))))
`(progn (shadow ',name)
(defstruct ,new-name ...)
(export ...)
...)))


--
Thomas A. Russ, USC/Information Sciences Institute
From: Jonathan Braud on
Thanks to the three of you. I guess it was high time I read a few things
on how the reader actually reads!

The solution Thomas gave appears to work.

It's the way to do it that feels the most natural to me. Still, I tried
to find one that involved read-time evaluation, and the only thing I
could come up with was calling the macro like this:

(defstruct-shadow-expand #.(progn (shadow 'lambda) 'lambda) ...)

It obviously makes the macro quite inconvenient to use. So I'm wondering
if you were thinking of something more clever. Although, unless I missed
something again, I really don't see how a macro could have any impact on
what happens at read time.