Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

unbound variable in macro

7 views
Skip to first unread message

Coby

unread,
Sep 29, 1999, 3:00:00 AM9/29/99
to
I am trying to write a little macro for debugging purposes. It is
intended to capture local variable values to a global. I would like to
use it as below:

(defun blah ()
(let ((x 11) (y 22))
(trap-let-list '((x 11) (y 22)) *foo*))
(...function code...)))

The idea being i could insert it in a function, copy and paste the let
list, redefine the function and next time it ran capture the data
values to the global of my choice.

I tried:
(defmacro trap-let-list (let-list global)
`(setf ,global (mapcar #, (mapcar #'car ,let-list))))

among many other straw-grasping things. I can not escape the "attempt
to evaluate the unbound variable 'x'" abortion, or ending up with *foo*
being (x y) and not (11 22).

There is something going on with scope that i don't understand. I
thought the comma inside a backquote would evaluate the variables....

Other approaches are always appreciated, but i do want to know why i
can't make this work.

Help?...

Coby

PS i have followed the recent thread about unintentional variable
capture, maybe the answer is buried in there, but i missed the
beginning and was not able to really understand the finer points.

Specifically: won't a 'let' in your macro avoid any dangers? (sorry
for mixing issues, but i'm wondering if the answers aren't very closely
related)


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

Rob Warnock

unread,
Sep 29, 1999, 3:00:00 AM9/29/99
to
Coby <cm...@my-deja.com> wrote:
+---------------

| I am trying to write a little macro for debugging purposes. It is
| intended to capture local variable values to a global. I would like to
| use it as below:
|
| (defun blah ()
| (let ((x 11) (y 22))
| (trap-let-list '((x 11) (y 22)) *foo*))
| (...function code...)))
|
| The idea being i could insert it in a function, copy and paste the let
| list, redefine the function and next time it ran capture the data
| values to the global of my choice.
|
| I tried:
| (defmacro trap-let-list (let-list global)
| `(setf ,global (mapcar #, (mapcar #'car ,let-list))))
|
| among many other straw-grasping things. I can not escape the "attempt
| to evaluate the unbound variable 'x'" abortion, or ending up with *foo*
| being (x y) and not (11 22).
+---------------

You're pretty close, actually. First, since TRAP-LET-LIST is a macro,
you don't need to (in fact, mustn't!) quote the "let-list" arg. Second,
I don't know what you meant by that "#," in the first MAPCAR, but the
following way of setting the result works:

> (defvar *foo*)
*FOO*
> (defmacro trap-let-list (let-list global)
`(setf ,global (list ,@(mapcar #'car let-list))))
TRAP-LET-LIST


> (defun blah ()
(let ((x 11) (y 22))

(incf x 7) ; simulate something happening...
(incf y) ; ditto
(trap-let-list ((x 11) (y 22)) *foo*)) ; now take snapshot
'the-end)
BLAH
> (blah)
THE-END
> *foo*
(18 23)
>

'Zat what you were looking for?


-Rob

-----
Rob Warnock, 8L-846 rp...@sgi.com
Applied Networking http://reality.sgi.com/rpw3/
Silicon Graphics, Inc. Phone: 650-933-1673
1600 Amphitheatre Pkwy. FAX: 650-933-0511
Mountain View, CA 94043 PP-ASEL-IA

David D. Smith

unread,
Sep 29, 1999, 3:00:00 AM9/29/99
to
In article <7srlr6$213$1...@nnrp1.deja.com>, Coby <cm...@my-deja.com> wrote:

> I am trying to write a little macro for debugging purposes. It is
> intended to capture local variable values to a global. I would like to
> use it as below:
>
> (defun blah ()
> (let ((x 11) (y 22))
> (trap-let-list '((x 11) (y 22)) *foo*))
> (...function code...)))
>
> The idea being i could insert it in a function, copy and paste the let
> list, redefine the function and next time it ran capture the data
> values to the global of my choice.
>
> I tried:
> (defmacro trap-let-list (let-list global)
> `(setf ,global (mapcar #, (mapcar #'car ,let-list))))
>
> among many other straw-grasping things. I can not escape the "attempt
> to evaluate the unbound variable 'x'" abortion, or ending up with *foo*
> being (x y) and not (11 22).

I am not ssure what you are looking for but try this for one possible
interpretation:

(defmacro trap-let-list (let-list global)
`(setf ,global (list ,.(mapcar #'car (eval let-list)))))

(trap-let-list '((x 11) (y 22)) *foo*) =X> (SETQ *FOO* (LIST X Y))


If you drop the quote in (trap-let-list '((x 11) (y 22)) ... then drop the
EVAL in TRAP-LET-LIST.

d

Coby

unread,
Sep 29, 1999, 3:00:00 AM9/29/99
to
In article <7ss4th$b8...@fido.engr.sgi.com>,
rp...@rigden.engr.sgi.com (Rob Warnock) wrote:
> Coby <cm...@my-deja.com> wrote:
> +---------------

> | I am trying to write a little macro for debugging purposes. It is
> | intended to capture local variable values to a global. I would
like to
> | use it as below:
> |
> | (defun blah ()
> | (let ((x 11) (y 22))
> | (trap-let-list '((x 11) (y 22)) *foo*))
> | (...function code...)))
> |
> | The idea being i could insert it in a function, copy and paste the
let
> | list, redefine the function and next time it ran capture the data
> | values to the global of my choice.
> |
> | I tried:
> | (defmacro trap-let-list (let-list global)
> | `(setf ,global (mapcar #, (mapcar #'car ,let-list))))
> |
> | among many other straw-grasping things. I can not escape
the "attempt
> | to evaluate the unbound variable 'x'" abortion, or ending up with
*foo*
> | being (x y) and not (11 22).
> +---------------
>
> You're pretty close, actually. First, since TRAP-LET-LIST is a macro,
> you don't need to (in fact, mustn't!) quote the "let-list" arg.
Second,
> I don't know what you meant by that "#," in the first MAPCAR, but the

I was meaning to apply the , as a function to each element of the list
of car's

>
> (defmacro trap-let-list (let-list global)
> `(setf ,global (list ,@(mapcar #'car let-list))))


>
> 'Zat what you were looking for?
>

Yes, thank you! I just couldn't seem to find the right combo of ,'s
and @'s....

Rob Warnock

unread,
Oct 4, 1999, 3:00:00 AM10/4/99
to
[My apologies in advance to Common Lisp purists for using certain
non-Common-Lisp Scheme terminology in what follows. I understand
that the terms "quasiquote", "unquote", and "unquote-splicing" are
*not* part of Common Lisp, however I have found that they are often
helpful in talking about the subforms of Common Lisps's "backquote"
syntax. The HyperSpec even acknowledges this (I think) with an explicit
reference to Scheme in "2.4.6.1 Notes about Backquote":

http://www.harlequin.com/education/books/HyperSpec/Body/sec_2-4-6-1.html

Erik may still yell at me (sobeit), but IMHO this is one case where
using Scheme terminology may be helpful to understanding Common Lisp.]

Coby <cm...@my-deja.com> wrote:
+---------------


| rp...@rigden.engr.sgi.com (Rob Warnock) wrote:
| > Coby <cm...@my-deja.com> wrote:
| > +---------------

| > | I tried:
| > | (defmacro trap-let-list (let-list global)
| > | `(setf ,global (mapcar #, (mapcar #'car ,let-list))))

| > +---------------


| >
| > I don't know what you meant by that "#," in the first MAPCAR, but the
|
| I was meaning to apply the , as a function to each element of the list
| of car's

+---------------

Uh... "," isn't a "function"!! It's shorthand *syntax* for [what Scheme calls]
an "unquote" operation (which isn't a function either -- it's also syntax).
Likewise, "`" and ",@" are shorthand syntax for the operations called [in
Scheme] "quasiquote" and "unquote-splicing", respectively. For example, my
suggested solution:

(defmacro trap-let-list (let-list global)
`(setf ,global (list ,@(mapcar #'car let-list))))

is [in Scheme] the same as:

(defmacro trap-let-list (let-list global)
(quasiquote
(setf (unquote global)
(list (unquote-splicing (mapcar #'car let-list))))))

which, after expansion, [in either Scheme or CL] is exactly the same as:

(defmacro trap-let-list (let-list global)
(list 'setf global (cons 'list (mapcar #'car let-list))))

[O.k., o.k., "exactly the same" only in the sense of "equal" to.
http://www.harlequin.com/education/books/HyperSpec/Body/sec_2-4-6.html ]

+---------------


| I just couldn't seem to find the right combo of ,'s and @'s....

+---------------

Suggestion: Whenever you get confused about that, hand-expand all the
quasiquotes, unquotes, and unquote-splicings according to the rules given
in the HyperSpec, Section 2-4-6, and see if what you have left makes sense.

In fact, I'll go even farther: Until you start feeling comfortable with
"defmacro", don't even *use* quasiquote, unquote, or unquote-splicing
at all! (At least for a little while...) They're just conveniences
(albeit *extreme* conveniences!), and aren't actually necessary at all.

Howard R. Stearns

unread,
Oct 4, 1999, 3:00:00 AM10/4/99
to
Do people find this quasiquote stuff usefull?

The Eclipse bacquote reader generates forms using public symbols like
QUASIQUOTE, UNQUOTE, UNQUOTE-SPLICING, UNQUOTE-SPLICING! and
QUASIVECTOR, each of which is an ordinary macro that can also be used
directly (in the right context).

I've never heard anyone comment on the utility of this. We did it only
because we figured that since we started from scratch, we might as well
be compatible with the Scheme names instead of the uninterned BQ-xxx
symbols that Steele used in the CLtL2 appendix, and which most
implementatins use a variant of.

If people find value in this, I'll post the reader and pretty printer
code so that everyone can use it. (It's nothing fancy, just a
modification of Steele's public domain code.)

Barry Margolin

unread,
Oct 4, 1999, 3:00:00 AM10/4/99
to
In article <37F8CCA0...@elwood.com>,

Howard R. Stearns <how...@elwood.com> wrote:
>Do people find this quasiquote stuff usefull?
>
>The Eclipse bacquote reader generates forms using public symbols like
>QUASIQUOTE, UNQUOTE, UNQUOTE-SPLICING, UNQUOTE-SPLICING! and
>QUASIVECTOR, each of which is an ordinary macro that can also be used
>directly (in the right context).
>
>I've never heard anyone comment on the utility of this. We did it only
>because we figured that since we started from scratch, we might as well
>be compatible with the Scheme names instead of the uninterned BQ-xxx
>symbols that Steele used in the CLtL2 appendix, and which most
>implementatins use a variant of.
>
>If people find value in this, I'll post the reader and pretty printer
>code so that everyone can use it. (It's nothing fancy, just a
>modification of Steele's public domain code.)

The main value of using weird, internal functions or macros for the
expansion of backquote is that it's convenient for the pretty-printer to
recognize them and regenerate backquotes in the output. If you use
exported symbols for this, the pretty-printer can't tell whether
occurrences of these symbols were probably typed directly or were the
result of read-macro expansion. When they're not exported, it's a more
reasonable assumption that they weren't typed directly, so it's appropriate
to translate them back to backquotes.

Unfortunately, the expansion of the "'" read-macro is required to be QUOTE,
so implementations can't use this trick. So if you do:

(pprint '(quote x))

the output might be:

'X

and if you display the source to the function containing that line, you may
see:

(print ''x)

I've seen people confused by this.

--
Barry Margolin, bar...@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.

Howard R. Stearns

unread,
Oct 4, 1999, 3:00:00 AM10/4/99
to
Thanks for the great explanation.

Of course, the flip side is that one might want to create something by
hand that gets printed in the same way as something produced by the
standard reader. Of course, even with uninterned symbols, one can still
get a hold of them and use them directly, so either need can be filled
with either implementation technique.

It strikes me, though, that a public symbol is easier to either use or
not use as you wish, then to try to get a hold of some uninterned and
system-dependent one. This is especially true of the public symbol has
a somewhat funny name and is not exported from the CL package.

So anyway, the point is that you are right, there is a downside to
making such symbols public and standardized. It's still not clear to
me, though, whether its worth it. (Any comments from the Scheme
community?)

Christopher R. Barry

unread,
Oct 5, 1999, 3:00:00 AM10/5/99
to
Barry Margolin <bar...@bbnplanet.com> writes:

> The main value of using weird, internal functions or macros for the
> expansion of backquote is that it's convenient for the pretty-printer to
> recognize them and regenerate backquotes in the output. If you use
> exported symbols for this, the pretty-printer can't tell whether
> occurrences of these symbols were probably typed directly or were the
> result of read-macro expansion.

I can remember from debugging some complicated macros last summer that
Allegro would sometimes annoyingly print backquoted forms involved
with macroexpand operations as (EXCL::BQ-QUOTE ...). CMU, Symbolics,
etc. never do this.

Christopher

Joerg-Cyril Hoehle

unread,
Oct 5, 1999, 3:00:00 AM10/5/99
to
"Howard R. Stearns" <how...@elwood.com> writes:

> The Eclipse bacquote reader generates forms using public symbols like
> QUASIQUOTE, UNQUOTE, UNQUOTE-SPLICING, UNQUOTE-SPLICING! and
> QUASIVECTOR, each of which is an ordinary macro that can also be used
> directly (in the right context).
>
> I've never heard anyone comment on the utility of this. We did it only

Well, so I'll do!

> because we figured that since we started from scratch, we might as well
> be compatible with the Scheme names instead of the uninterned BQ-xxx

Excellent idea! (never used Eclipse however)


> symbols that Steele used in the CLtL2 appendix, and which most
> implementatins use a variant of.

I also find a documented interface to backquotig like in Scheme much
superior to implementation-dependent system macros or functions like
in CL implementations. I've seen neat extensions of comma structures
for your own applications that can be written portably in Scheme but
not in CL.

Any drawback to Scheme's explicitly namnig the constructors?

Regards,
J"org H"ohle
Forschungszentrum Telekom AG/Telekom Research Center

Howard R. Stearns

unread,
Oct 6, 1999, 3:00:00 AM10/6/99
to

I would imagine that all default pretty-printers provided by the
implementation would always display the implementation-specific BQ-QUOTE
sort of forms as backquotes, and that normal print would not (assuming
all the right printer control variables had suitable values).

For example, in CMUCL:

* (write (read-from-string "`(foo ,bar)") :pretty nil)
(COMMON-LISP::BACKQ-LIST (QUOTE FOO) BAR)
`(FOO ,BAR)

Does the annoyance come from the fact that BQ-QUOTE and friends:
+ ever get printed at all?
+ sometimes do and sometimes don't, and it isn't always clear why?
+ that the symbol printed is some implementation-specific thing.
+ some other reason?

If one of the first two, how is this different than having (quote x)
sometimes print as (QUOTE X) and sometimes as 'X, depending on
pretty-printing, etc., or is (QUOTE X) equally annoying.

[here's one of those lame equivocations: I just read over this, and I
see that it might look like I'm trying to convince you of something.
I'm not, but I don't know how to quickly fix the wording. Please let it
suffice to say that I'm just trying to understand what the possible
issues might be.]

Howard R. Stearns

unread,
Oct 8, 1999, 3:00:00 AM10/8/99
to
Joerg-Cyril Hoehle wrote:
>
> "Howard R. Stearns" <how...@elwood.com> writes:
>
> > The Eclipse bacquote reader generates forms using public symbols like
> > QUASIQUOTE, UNQUOTE, UNQUOTE-SPLICING, UNQUOTE-SPLICING! and
> > QUASIVECTOR, each of which is an ordinary macro that can also be used
> > directly (in the right context).
> >
> > I've never heard anyone comment on the utility of this. We did it only
> Well, so I'll do!
>
> > because we figured that since we started from scratch, we might as well
> > be compatible with the Scheme names instead of the uninterned BQ-xxx
> Excellent idea! (never used Eclipse however)
> > symbols that Steele used in the CLtL2 appendix, and which most
> > implementatins use a variant of.
>
> I also find a documented interface to backquotig like in Scheme much
> superior to implementation-dependent system macros or functions like
> in CL implementations. I've seen neat extensions of comma structures
> for your own applications that can be written portably in Scheme but
> not in CL.

I broached this issue rather hastily, without thinking it through.

One complication is that there are two sets of symbols that might be
created:

1. QUASIQUOTE and friends, as in Scheme, can be used directly by
programmers, and I think it is possible that the reader can generate
these from the #\` and #\, reader macros. QUASIQUOTE forms can be
pretty-printed to use the backquote characters.

2. To process QUASIQUOTE, one goes over the list structure ala Steele
and produces, essentially, expressions involving LIST, QUOTE, APPEND,
NCONC, etc. Now, if one wants to be able to have these expressions
pretty-printed using backquote characters, it is necessary to not
process QUASIQUOTE into LIST, QUOTE, etc., but into another set of
symbols such as BQ-LIST, BQ-QUOTE, etc. This isn't just desirable
(e.g., so that for example, all list data with a LIST car don't get
printed this way), but it is NECESSARY because random list data might
not be correctly processable this way -- i.e., there might be errors
converting it back into QUASIQUOTE form for pretty printing.

In looking back over my code, I see that explicit QUASIQUOTE forms are
pretty-printed as backquote, and they are processed to BQ-LIST, etc. at
macroexpansion time. However, the #\` reader macro processes things to
BQ-LIST form at READ time, so that errors in reading are caught at read
time, instead of, for example, creating QUASIQUOTE forms in which errors
will not be caught until macro expansion.

I'm not at all sure what the right thing is here, so again, if anyone
wants the code to play with, let me know.

>
> Any drawback to Scheme's explicitly namnig the constructors?

Well, now that I've muddied the waters with two sets of constructors, a
new issue is polution of the space of publicly named things.

Barmar has also pointed out that accidental use of list structure that
uses the plublicly named things for other purposes can cause problems,
and the issue about problems converting BQ-LIST to QUASIQUOTE emphasizes
the risk.

Christopher R. Barry

unread,
Oct 8, 1999, 3:00:00 AM10/8/99
to
hoehle...@tzd.dont.telekom.spam.de.me (Joerg-Cyril Hoehle) writes:

> I also find a documented interface to backquotig like in Scheme much
> superior to implementation-dependent system macros or functions like
> in CL implementations. I've seen neat extensions of comma structures
> for your own applications that can be written portably in Scheme but
> not in CL.

Could you give examples of these "neat extensions"?

FWIW I once worked on a macro that actually generated nested
MACROLETs. The number of macrolet forms generated varied on the input
so I wanted to be able to conditionally choose whether to put "," or
",," etc. in front of a list. The backquote-comma syntax adds no new
primitive functionality though; it's just prettier to say

`(let* ((,evaled-value ,object)
,@(mapcar #'list vars vals))
...)

instead of[1]

(append (list 'let*)
(list (append (list (append (list evaled-value) (list object) 'nil))
(mapcar #'list vars vals) 'nil))
(list (append (list 'quote) (list ...) 'nil)) 'nil)

So it was still possible to write the macrolet; it's just _a lot_
harder to have the macro procedurally build those lists. It's so rare
that you need to do this though that I don't think this is much of an
issue.

Here are a few examples of the semi-EVAL-like things you can do with
MACROLETs in macro bodies:

(defmacro depth-1-eval (form)
`(macrolet ((return-1 ()
,form))
(return-1)))

(defmacro depth-2-eval (form)
`(macrolet ((return-1 ()
`(macrolet ((return-2 ()
,,form))
(return-2))))
(return-1)))

(defmacro depth-3-eval (form)
`(macrolet ((return-1 ()
`(macrolet ((return-2 ()
`(macrolet ((return-3 ()
,,,form))
(return-3))))
(return-2))))
(return-1)))

Now:

USER(4): (setq depth-0 'depth-1)
DEPTH-1

USER(5): (setq depth-1 'depth-2)
DEPTH-2

USER(6): (setq depth-2 'depth-3)
DEPTH-3

USER(7): (setq depth-3 'depth-4)
DEPTH-4

USER(8): (eval 'depth-0)
DEPTH-1

USER(9): (depth-1-eval 'depth-0)
DEPTH-1

USER(10): (eval (eval 'depth-0))
DEPTH-2

USER(11): (depth-2-eval 'depth-0)
DEPTH-2

USER(12): (eval (eval (eval 'depth-0)))
DEPTH-3

USER(13): (depth-3-eval 'depth-0)
DEPTH-3

Now imagine trying to construct a macro that takes a numeric argument
N and returns a DEPTH-N-EVAL macro like the above three. Since you
can't generate those commas and backquotes from Lisp you'd have a
lot harder time.

Here is a semi-practical example of putting this to use; using a
nested MACROLET to allow arguments to DEFCLASS to be evaluated (kinda;
this is _not_ equivalent to using EVAL...):

(defmacro defclass* (name supers slots &rest options)
`(macrolet ((do-it ()
`(defclass ,,name ,,supers ,,slots ,,@options)))
(do-it)))

Now you can do something like:

USER(15): (loop for i from 1 to 3
collect (defclass* (intern (format nil "FOO-~D" i)) () ()))
(#<STANDARD-CLASS FOO-1> #<STANDARD-CLASS FOO-2> #<STANDARD-CLASS FOO-3>)

These MACROLET hacks as I've presented them aren't really magical or
useful once you understand what's really going on, but there are times
when conditionally generating nested backquote-comma structure in a
macro (like with a MACROLET) is what you really want to do. (I'm just
too lazy to come up with a good example, since a good example would be
pretty complicated.)

Christopher


Footnotes:

1. This is roughly the output of Guy Steele's backquote implementation
in the back of CLTL. It really need not be any more complicated than:

(list 'let* (list* (list evaled-value object) (mapcar #'list vars vals)))

0 new messages