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

WITH-GENSYMS specification

40 views
Skip to first unread message

Vebjorn Ljosa

unread,
Nov 15, 2001, 3:07:25 AM11/15/01
to
WITH-GENSYMS is one of those macros that are written over and over.
Though there aren't that many variations---compared to SPLIT-SEQUENCE,
EXTREMUM or the collection utilities---it might be good to nail down a
specification and include it in Common Lisp Utilities.

Here's a first draft for a specification and reference implementation.
Your comments are welcome.


Macro WITH-GENSYMS

Syntax:

WITH-GENSYMS ( { var | (var x) }* ) declaration* form* => result*

Arguments and Values:

var---a symbol.
x---a string or a non-negative integer.
declaration---a declare expression; not evaluated.
form---a form.
results---the values returned by the forms.

Description:

Creates new variable bindings for the vars and executes a series
of forms that use these bindings.

The variable bindings created are lexical unless special
declarations are specified.

Each of the vars is bound to a fresh, uninterned symbol, as if
returned by a call to GENSYM---with x as argument to GENSYM if x
is supplied, otherwise without arguments. The vars are bound to
the values over the execution of the forms, which make up an
implicit progn. The consequences are unspecified if a type
declaration is specified for a var, but the value to which that
var is bound is not consistent with the type declaration.

The scopes of the name binding and declarations do not include the
xs.

Examples:

(with-gensyms (sym1) sym1) => #:G3142
(with-gensyms (sym2) (symbol-package sym2)) => NIL
(with-gensyms ((sym3 "T")) sym3) => #:T3143
(with-gensyms ((sym4 100)) (setf sym5 sym4)) => #:G100
(with-gensyms ((sym6 100)) (setf sym7 sym6)) => #:G100
(eq sym5 sym7) => NIL
(find-symbol "G100") => NIL, NIL
(with-gensyms (sym8) (eq sym8 sym8)) => T
(with-gensyms (sym9) (set sym9 42) (symbol-value sym9)) => 42

Affected By: None.

Exceptional Situations: None.

See Also:

GENSYM, LET

Notes:

The ability to pass a numeric argument as x is deprecated;
explicitly binding *gensym-counter* is now stylistically
preferred. (The somewhat baroque conventions for the optional
argument are historical in nature, and supported primarily for
compatibility with older dialects of Lisp. In modern code, it is
recommended that the only kind of argument used be a string
prefix.)

[Should we remove the ability to pass an integer alltogether?]

Status:

First draft posted to comp.lang.lisp for discussion. Suggested
for eventual inclusion in the layered community standard known as
Common Lisp Utilities.

Reference Implementation:

(defmacro with-gensyms ((&rest bindings) &body body)
`(let ,(mapcar #'(lambda (binding)
(check-type binding (or cons symbol))
(if (consp binding)
(destructuring-bind (var x) binding
(check-type var symbol)
(check-type x (or string integer))
`(,var (gensym ,x)))
`(,binding (gensym))))
bindings)
,@body))

--
Vebjorn Ljosa

Pekka P. Pirinen

unread,
Nov 16, 2001, 10:45:15 AM11/16/01
to
> Macro WITH-GENSYMS

This is a useful thing to have and there's nothing in the spec to
criticize (though the ability to pass an integer is really not useful,
as you suspected).

It is, however, rather similar to LispWorks' WITH-UNIQUE-NAMES. See
<http://www.xanalys.com/software_tools/reference/lwl41/lwref/LWRM_247.HTM>.
(This is also available in Liquid Lisp.) Since it was conceived as a
macro writing tool, it doesn't take arguments for GENSYM, just uses
the names of the variables themselves.

I prefer the simplicity of WITH-UNIQUE-NAMES, but I'm biased since I
put it in the implementation many years ago. Are there uses of
WITH-GENSYMS where the ability to provide an arbitrary string is
useful?
--
Pekka P. Pirinen, Global Graphics Software
"A man never speaks of himself without losing something. What he says
in his disfavor is always believed but when he commends himself, he arouses
mistrust." - Michel Montaigne

Raymond Toy

unread,
Nov 16, 2001, 12:01:59 PM11/16/01
to
>>>>> "Vebjorn" == Vebjorn Ljosa <ab...@ljosa.com> writes:

Vebjorn> Syntax:

Vebjorn> WITH-GENSYMS ( { var | (var x) }* ) declaration* form* => result*

Vebjorn> Arguments and Values:

Vebjorn> var---a symbol.
Vebjorn> x---a string or a non-negative integer.
Vebjorn> declaration---a declare expression; not evaluated.
Vebjorn> form---a form.
Vebjorn> results---the values returned by the forms.

[snip]

Vebjorn> Each of the vars is bound to a fresh, uninterned symbol, as if
Vebjorn> returned by a call to GENSYM---with x as argument to GENSYM if x
Vebjorn> is supplied, otherwise without arguments. The vars are bound to

I would prefer that if x is not given, then var is used as the arg to
gensym. This makes it easier to figure out that the var STUFF is
#:STUFF1234 instead of #:G8662. :-)

Ray

Christophe Rhodes

unread,
Nov 16, 2001, 1:00:14 PM11/16/01
to
Raymond Toy <t...@rtp.ericsson.se> writes:

I'd second this -- and it seems to take care of Pekka's point, too,
doesn't it?

Christophe
--
Jesus College, Cambridge, CB5 8BL +44 1223 510 299
http://www-jcsu.jesus.cam.ac.uk/~csr21/ (defun pling-dollar
(str schar arg) (first (last +))) (make-dispatch-macro-character #\! t)
(set-dispatch-macro-character #\! #\$ #'pling-dollar)

Thomas F. Burdick

unread,
Nov 16, 2001, 3:58:14 PM11/16/01
to
Raymond Toy <t...@rtp.ericsson.se> writes:

I agree, too. My only other comment is that the WITH-GENSYMS that I
use allows X to be a symbol, and, if so, uses its SYMBOL-NAME. This
is to allow the use of keywords as strings, which I personally like.

--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'

Vebjorn Ljosa

unread,
Nov 16, 2001, 3:59:29 PM11/16/01
to
* Vebjorn Ljosa <ab...@ljosa.com>

| WITH-GENSYMS is one of those macros that are written over and over.
| Though there aren't that many variations---compared to SPLIT-SEQUENCE,
| EXTREMUM or the collection utilities---it might be good to nail down a
| specification and include it in Common Lisp Utilities.

Jörg Höhle (who asked that his email address not be revealed) wrote to
me privately, calling my attention to these articles from a couple of
years back by Kent M. Pitman and Howard R. Stearns.

- Vebjorn


From pit...@world.std.com Mon Feb 1 17:12:26 1999
Newsgroups: comp.lang.lisp
Path: mailgate99.telekom.de!jupiter.NIC.DTAG.DE!newsfeed.ecrc.net!btnet-peer!btnet!rill.news.pipex.net!pipex!ams.uu.net!ffx.uu.net!in2.uu.net!world!pitman
From: Kent M Pitman <pit...@world.std.com>
Subject: Re: Defining macros ?
Sender: pit...@world.std.com (Kent M Pitman)
Message-ID: <sfw67a7...@world.std.com>
References: <77od29$jev$1...@black.news.nacamar.net>
<sfwsodc...@world.std.com>
Organization: The World Public Access UNIX, Brookline, MA
X-Newsreader: Gnus v5.3/Emacs 19.34

Incidentally, harlequin uses something called REBINDING which the Lisp
Machine had a similar variation called ONCE-ONLY. This is a case
where the LispM screwed up, I think, and made its solution overly
complicated (and I have always assumed it was because the Lisp Machine
compiler had a bias toward not liking extra lambda bindings to be
created; the compiler for Harlequin is pretty good about folding out
gratuitous lambda bindings, so it's fine for macros to introduce
them--once again an argument for letting the market sort out tiny details
like this). Anyway, what is normally written by people as:

(defmacro foo (var1 var2)
(let ((v1 (gensym "VAR1"))
(v2 (gensym "VAR2")))
`(let ((,v1 ,var1)
(,v2 ,var2))
(+ (* ,v1 ,v1) (* ,v2 ,v2)))))

would be written using REBINDING as follows:

(defmacro foo (var1 var2 &body forms)
(rebinding (var1 var2)
`(+ (* ,var1 ,var1) (* ,var2 ,var2))))

ONCE-ONLY is equivalent, but for some circumstances
involving macros, you have to pass through an environment
argument, as in:

(defmacro foo (f g &environment env)
(once-only (f g &environment env)
...))

The &ENVIRONMENT was about the fact that ONCE-ONLY doesn't expand
into the LET forms that REBINDING does because it knows the compiler
won't like those bindings, so it tree-walks the body and does the
optimizations associated with finding out if the extra binding
is needed at macro expand time (using techniques already available
in the system and used in the compiler). But by just using the
bindings that REBINDING does, it's not necessary to do all that
hair. A portable implementation of REBINDING can be written in a
few lines of code (ok, maybe about 20 lines).

It's sad that it's not part of CL. I made a comment about its
absence during the Public Review of the X3J13 draft; this caused
it to come up for a vote of the committee, but it was voted down.

Incidentally, REBINDING has a friend in the gensym arena. Using it,

(with-unique-names (a b c) `(let ((,a ...)) ...))

is like

(let ((a (gensym "A")) (b (gensym "B")) ...) `(let ((,a ...)) ...))

It is also absent and it's a shame. It's a majorly useful tool
to the macro writer.

(If it helps think about it, there is loose analogy of the form
REBINDING is to LET as WITH-UNIQUE-NAMES is to LAMBDA. If it doesn't
help you, don't try--some people like this analogy, some hate it. But
my entire brain is one big analogy engine, and I see analogies where
others often think I'm off the deep end and I do this compulsively.
REBINDING does both the introduction of variables and the setup of
binding; WITH-UNIQUE-NAMES sets up the bindings but doesn't
immediately use them. Pretty much as do LET and LAMBDA. Heh. Well,
it helps to see why there are two of these and both are important. Or
it helps me anyway. Unlike LET and LAMBDA, though, I don't recommend
trying to build up one from the other. It just makes a mess; or, at
least, it has made a mess every time I've tried. Then again, DO and
DO* also have analogous relationships and yet can't be built from one
another, so it's not fatal to the idea that there's a relationship.
Never mind any of this parenthetical remark if you don't see what I'm
getting at. It's late and I'm perhaps rambling again.)


From how...@elwood.com Mon Feb 1 17:14:27 1999
Path: mailgate99.telekom.de!jupiter.NIC.DTAG.DE!newsfeed.nacamar.de!wn4feed!135.173.83.24!wn3feed!worldnet.att.net!206.214.99.1!ix.netcom.com!news
From: "Howard R. Stearns" <how...@elwood.com>
Newsgroups: comp.lang.lisp
Subject: Re: Defining macros ?
Organization: Elwood Corp. (http://www.elwood.com/eclipse-info)
Message-ID: <36A35A9E...@elwood.com>
References: <77od29$jev$1...@black.news.nacamar.net>
<sfwsodc...@world.std.com> <sfw67a7...@world.std.com>
NNTP-Posting-Host: mil-wi9-24.ix.netcom.com
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-NETCOM-Date: Mon Jan 18 10:02:32 AM CST 1999
X-Mailer: Mozilla 4.04 [en] (X11; I; SunOS 5.6 sun4c)

Here's some code I wrote for WITH-UNIQUE-NAMES and REBINDING. It would
probably not be right for me to say that this is how these are
implemented in Eclipse, so I won't. Documentation for the Eclipse
versions are on-line at:

http://www.elwood.com/eclipse/unique.htm
http://www.elwood.com/eclipse/rebinding.htm

(defmacro WITH-UNIQUE-NAMES (vars &body body)
`(let ,(loop for var in vars
collect `(,var (make-symbol ,(symbol-name var))))
,@body))

(defmacro REBINDING (vars &body body) ;difficult code here!..
(loop for var in vars
for name = (make-symbol (symbol-name var))
collect `(,name ,var) into renames
collect ``(,,var ,,name) into temps
finally (return `(let ,renames
(with-unique-names ,vars
`(let (,,@temps)
,,@body))))))

Speaking for Elwood Corporation, we have no problem with Lisp
implementors standardizing on such utilities or their documentation.
Anyone should feel free to use these defs (supplied by Howard,
individually, not Elwood) or to "quote from" the on-line documentation.
No warantees on their fitness, etc., etc.... Just program responsibly.

In return, we welcome any comments, bug reports, etc.

I'm also curious as to whether people prefer that temps be created with
MAKE-SYMBOL, as above, or GENSYM. GENSYM obviously helps in nested uses
if you have *PRINT-CIRCLE* turned off, but personally, I'd rather not
have the hairy looking numbers, and I use *print-circle* when I need to.


Kent M Pitman wrote:
>
> Incidentally, harlequin uses something called REBINDING which the Lisp
> Machine had a similar variation called ONCE-ONLY. This is a case
> where the LispM screwed up, I think, and made its solution overly
> complicated (and I have always assumed it was because the Lisp Machine
> compiler had a bias toward not liking extra lambda bindings to be
> created; the compiler for Harlequin is pretty good about folding out
> gratuitous lambda bindings, so it's fine for macros to introduce
> them--once again an argument for letting the market sort out tiny details
> like this). Anyway, what is normally written by people as:
>
> (defmacro foo (var1 var2)
> (let ((v1 (gensym "VAR1"))
> (v2 (gensym "VAR2")))
> `(let ((,v1 ,var1)
> (,v2 ,var2))
> (+ (* ,v1 ,v1) (* ,v2 ,v2)))))
>
> would be written using REBINDING as follows:
>
> (defmacro foo (var1 var2 &body forms)
> (rebinding (var1 var2)
> `(+ (* ,var1 ,var1) (* ,var2 ,var2))))
>
> ONCE-ONLY is equivalent, but for some circumstances
> involving macros, you have to pass through an environment
> argument, as in:
>
> (defmacro foo (f g &environment env)
> (once-only (f g &environment env)
> ...))
>
> The &ENVIRONMENT was about the fact that ONCE-ONLY doesn't expand
> into the LET forms that REBINDING does because it knows the compiler
> won't like those bindings, so it tree-walks the body and does the
> optimizations associated with finding out if the extra binding
> is needed at macro expand time (using techniques already available
> in the system and used in the compiler). But by just using the
> bindings that REBINDING does, it's not necessary to do all that
> hair. A portable implementation of REBINDING can be written in a
> few lines of code (ok, maybe about 20 lines).
>
> It's sad that it's not part of CL. I made a comment about its
> absence during the Public Review of the X3J13 draft; this caused
> it to come up for a vote of the committee, but it was voted down.
>
> Incidentally, REBINDING has a friend in the gensym arena. Using it,
>
> (with-unique-names (a b c) `(let ((,a ...)) ...))
>
> is like
>
> (let ((a (gensym "A")) (b (gensym "B")) ...) `(let ((,a ...)) ...))
>
> It is also absent and it's a shame. It's a majorly useful tool
> to the macro writer.
>
> (If it helps think about it, there is loose analogy of the form
> REBINDING is to LET as WITH-UNIQUE-NAMES is to LAMBDA. If it doesn't
> help you, don't try--some people like this analogy, some hate it. But
> my entire brain is one big analogy engine, and I see analogies where
> others often think I'm off the deep end and I do this compulsively.
> REBINDING does both the introduction of variables and the setup of
> binding; WITH-UNIQUE-NAMES sets up the bindings but doesn't
> immediately use them. Pretty much as do LET and LAMBDA. Heh. Well,
> it helps to see why there are two of these and both are important. Or
> it helps me anyway. Unlike LET and LAMBDA, though, I don't recommend
> trying to build up one from the other. It just makes a mess; or, at
> least, it has made a mess every time I've tried. Then again, DO and
> DO* also have analogous relationships and yet can't be built from one
> another, so it's not fatal to the idea that there's a relationship.
> Never mind any of this parenthetical remark if you don't see what I'm
> getting at. It's late and I'm perhaps rambling again.)

Samir Sekkat

unread,
Nov 18, 2001, 3:26:02 AM11/18/01
to
In article <cy3eln0...@ljosa.com>, ab...@ljosa.com says...

> WITH-GENSYMS ( { var | (var x) }* ) declaration* form* => result*
>
> Arguments and Values:
>
> var---a symbol.
> x---a string or a non-negative integer.
> declaration---a declare expression; not evaluated.
> form---a form.
> results---the values returned by the forms.

> (with-gensyms (sym1) sym1) => #:G3142


> (with-gensyms (sym2) (symbol-package sym2)) => NIL
> (with-gensyms ((sym3 "T")) sym3) => #:T3143

If x is not specified, it could have the variable name as default value
IMHO it greatly eases the understanding of expanded code

BTW have anyone already implemented a way to find out very easely from
which macro a gensym was created?

I dont know yet emacs, such facilities might already exist there.

Vebjorn Ljosa

unread,
Nov 23, 2001, 12:52:35 AM11/23/01
to
* Vebjorn Ljosa <ab...@ljosa.com>

| WITH-GENSYMS is one of those macros that are written over and over.
| Though there aren't that many variations---compared to SPLIT-SEQUENCE,
| EXTREMUM or the collection utilities---it might be good to nail down a
| specification and include it in Common Lisp Utilities.
|
| Here's a first draft for a specification and reference implementation.
| Your comments are welcome.

Here's a revised version. I didn't know about the name
WITH-UNIQUE-NAMES, but now that Pekka brought it to my attention, I
think it's a better name than WITH-GENSYMS, so I've changed the name
in the specification. If somebody has strong feelings for
the WITH-GENSYMS name, now is the time to speak up and defend it.

Other changes:
* x can now be a string designator (character, symbol or string), not
only a string.
* If x is not supplied, var is the default.
* It's no longer possible for x to be an integer.

I'll post a spec for REBINDING under separate cover.

- Vebjorn


Macro WITH-UNIQUE-NAMES

Syntax:

WITH-UNIQUE-NAMES ( { var | (var x) }* ) declaration* form*
=> result*

Arguments and Values:

var---a symbol.
x---a string designator. The default is var.


declaration---a declare expression; not evaluated.
form---a form.
results---the values returned by the forms.

Description:

Executes a series of forms with each var bound to a fresh,
uninterned symbol. The uninterned symbol is as if returned by a
call to GENSYM with the string denoted by x---or, if x is not
supplied, the string denoted by var---as argument.

The variable bindings created are lexical unless special
declarations are specified.

The scopes of the name bindings and declarations do not include
the xs.

The forms are evaluated in order, and the values of all but the
last are discarded (that is, the body is an implicit progn).

Examples:

(with-unique-names (sym1) sym1) => #:SYM13142
(with-unique-names ((sym1 "SYM1-")) sym1) => #:SYM1-3143
(find-symbol "SYM1-3143") => NIL, NIL
(with-unique-names ((sym #\Q)) sym) => #:Q3144
(with-unique-names ((sym1 :sym1-)) sym1) => #:SYM1-3145
(with-unique-names (sym1) (symbol-package sym1)) => NIL
(with-unique-names (sym8) (eq sym8 sym8)) => T
(with-unique-names (sym9) (set sym9 42) (symbol-value sym9)) => 42

Affected By: None.

Exceptional Situations: None.

See Also:

GENSYM, LET, REBINDING

Status:

Second draft posted to comp.lang.lisp for discussion. (In the
first draft, the macro was called WITH-GENSYMS.) Suggested for


eventual inclusion in the layered community standard known as
Common Lisp Utilities.

Reference Implementation:

(defmacro with-unique-names ((&rest bindings) &body body)


`(let ,(mapcar #'(lambda (binding)
(check-type binding (or cons symbol))
(if (consp binding)
(destructuring-bind (var x) binding
(check-type var symbol)

`(,var (gensym ,(etypecase x
(symbol (symbol-name x))
(character (string x))
(string x)))))
`(,binding (gensym ,(symbol-name binding)))))

Nils Goesche

unread,
Nov 23, 2001, 6:08:18 AM11/23/01
to
Vebjorn Ljosa <ab...@ljosa.com> writes:

> * Vebjorn Ljosa <ab...@ljosa.com>
> | WITH-GENSYMS is one of those macros that are written over and over.
> | Though there aren't that many variations---compared to SPLIT-SEQUENCE,
> | EXTREMUM or the collection utilities---it might be good to nail down a
> | specification and include it in Common Lisp Utilities.
>

> Here's a revised version. I didn't know about the name
> WITH-UNIQUE-NAMES, but now that Pekka brought it to my attention, I
> think it's a better name than WITH-GENSYMS, so I've changed the name
> in the specification. If somebody has strong feelings for
> the WITH-GENSYMS name, now is the time to speak up and defend it.

I prefer with-gensyms because it's clearer what's going on.
Sometimes it's hard to guess the name of Common Lisp functions and
macros; if you do (apropos 'gensym) you won't find with-unique-names.

And it's shorter :-)

Regards,
--
Nils Goesche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x42B32FC9

Jochen Schmidt

unread,
Nov 23, 2001, 10:47:52 AM11/23/01
to
Nils Goesche wrote:

> Vebjorn Ljosa <ab...@ljosa.com> writes:
>
>> * Vebjorn Ljosa <ab...@ljosa.com>
>> | WITH-GENSYMS is one of those macros that are written over and over.
>> | Though there aren't that many variations---compared to SPLIT-SEQUENCE,
>> | EXTREMUM or the collection utilities---it might be good to nail down a
>> | specification and include it in Common Lisp Utilities.
>>
>> Here's a revised version. I didn't know about the name
>> WITH-UNIQUE-NAMES, but now that Pekka brought it to my attention, I
>> think it's a better name than WITH-GENSYMS, so I've changed the name
>> in the specification. If somebody has strong feelings for
>> the WITH-GENSYMS name, now is the time to speak up and defend it.
>
> I prefer with-gensyms because it's clearer what's going on.
> Sometimes it's hard to guess the name of Common Lisp functions and
> macros; if you do (apropos 'gensym) you won't find with-unique-names.

I disagree - WITH-GENSYMS is talking about what you would need while
WITH-UNIQUE-NAMES is talking about what you _want_ . If you already know
what you need then _finding_ a solution is no problem anymore...

> And it's shorter :-)

I don't think that WITH-UNIQUE-NAMES would be used so extensively in code to
justify optimizing for a short name...

ciao,
Jochen

--
http://www.dataheaven.de

Pierre R. Mai

unread,
Nov 23, 2001, 10:03:14 AM11/23/01
to
Vebjorn Ljosa <ab...@ljosa.com> writes:

> Here's a revised version. I didn't know about the name
> WITH-UNIQUE-NAMES, but now that Pekka brought it to my attention, I
> think it's a better name than WITH-GENSYMS, so I've changed the name
> in the specification. If somebody has strong feelings for
> the WITH-GENSYMS name, now is the time to speak up and defend it.

Great work! This and REBINDING is definitely something that should be
promoted to more wide-spread use. I'll interleave my comments
below...

> Macro WITH-UNIQUE-NAMES
>
> Syntax:
>
> WITH-UNIQUE-NAMES ( { var | (var x) }* ) declaration* form*
> => result*

I'd suggest using something more perspicuous than x, e.g. prefix:

WITH-UNIQUE-NAMES ( { var | (var prefix) }* ) declaration* form*
=> result*

> Arguments and Values:
>
> var---a symbol.
> x---a string designator. The default is var.

prefix---a string designator. The default is var.

> declaration---a declare expression; not evaluated.
> form---a form.
> results---the values returned by the forms.
>
> Description:
>
> Executes a series of forms with each var bound to a fresh,
> uninterned symbol. The uninterned symbol is as if returned by a

uninterned symbol. The uninterned symbol is created as if by a

Seems more readable, at least to my untrained eye...

> call to GENSYM with the string denoted by x---or, if x is not
> supplied, the string denoted by var---as argument.

call to GENSYM with the string denoted by prefix---or, if prefix


is not supplied, the string denoted by var---as argument.

> The variable bindings created are lexical unless special
> declarations are specified.
>
> The scopes of the name bindings and declarations do not include
> the xs.

the prefix forms.

That said, you haven't specified if either the var or the prefix forms
are evaluated. The var forms obviously aren't, but the prefix forms
might be. If they aren't (like in your sample implementation), then
the scope issue seems moot.

> The forms are evaluated in order, and the values of all but the
> last are discarded (that is, the body is an implicit progn).
>
> Examples:
>
> (with-unique-names (sym1) sym1) => #:SYM13142
> (with-unique-names ((sym1 "SYM1-")) sym1) => #:SYM1-3143
> (find-symbol "SYM1-3143") => NIL, NIL
> (with-unique-names ((sym #\Q)) sym) => #:Q3144
> (with-unique-names ((sym1 :sym1-)) sym1) => #:SYM1-3145
> (with-unique-names (sym1) (symbol-package sym1)) => NIL
> (with-unique-names (sym8) (eq sym8 sym8)) => T
> (with-unique-names (sym9) (set sym9 42) (symbol-value sym9)) => 42
>
> Affected By: None.
>

This should probably be:

Side Effects:

Might increment *gensym-counter*.

Affected By:

*gensym-counter*

> Exceptional Situations: None.
>
> See Also:
>
> GENSYM, LET, REBINDING
>
> Status:
>
> Second draft posted to comp.lang.lisp for discussion. (In the
> first draft, the macro was called WITH-GENSYMS.) Suggested for
> eventual inclusion in the layered community standard known as
> Common Lisp Utilities.
>
> Reference Implementation:
>
> (defmacro with-unique-names ((&rest bindings) &body body)
> `(let ,(mapcar #'(lambda (binding)
> (check-type binding (or cons symbol))
> (if (consp binding)
> (destructuring-bind (var x) binding
> (check-type var symbol)
> `(,var (gensym ,(etypecase x
> (symbol (symbol-name x))
> (character (string x))
> (string x)))))
> `(,binding (gensym ,(symbol-name binding)))))
> bindings)
> ,@body))

Might I suggest

(defmacro with-unique-names ((&rest bindings) &body body)
`(let ,(mapcar #'(lambda (binding)

(destructuring-bind (var prefix)
(if (consp binding) binding (list binding binding))
`(,var (gensym ,(string prefix)))))
bindings)
,@body))

This delegates error checking to relevant system primitives, like
destructuring-bind, string and let. This is good style, because
implementations usually know best what is e.g. a valid string
designator, or variable, _and_ we didn't specify any exceptional
situations of our own, but the given example implementation might give
the false impression that implementors should check for those specific
errors.

Regs, Pierre.

--
Pierre R. Mai <pm...@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein

Pierre R. Mai

unread,
Nov 23, 2001, 10:14:08 AM11/23/01
to
Nils Goesche <car...@cartan.de> writes:

While that is true, I think that WITH-UNIQUE-NAMES and GENSYM don't
have anything in common, except that one is implemented in terms of
the other. I.e. you don't expect to find all list using functions
when you use (apropos 'list) either.

It is a statement to the need for a WITH-UNIQUE-NAMES that people
think in terms of the much too low-level GENSYM when they want to
create unique variable names. Decoupling these seems to me to be a
good thing, even if it inconveniences old hands that are used to
thinking in terms of GENSYM.

The other reason is that the current specification of
WITH-UNIQUE-NAMES is upwardly compatible with other specs for W-U-N
that I've seen, so that would allow current usage of that macro to be
grand-fathered in, without having to manually convert code bodies.

Kalle Olavi Niemitalo

unread,
Nov 23, 2001, 2:28:19 PM11/23/01
to
Vebjorn Ljosa <ab...@ljosa.com> writes:

> * x can now be a string designator (character, symbol or string), not
> only a string.

I think the reference implementation should use (string x)
without checking the type of X, so that it allows
implementation-defined string designators too.

> * It's no longer possible for x to be an integer.

PREFIX might be a better name then.

> Affected By: None.

GENSYM is affected by *GENSYM-COUNTER*.
I don't know if that deserves being mentioned here.

Vebjorn Ljosa

unread,
Nov 23, 2001, 5:16:34 PM11/23/01
to
* "Pierre R. Mai" <pm...@acm.org>

| > WITH-UNIQUE-NAMES ( { var | (var x) }* ) declaration* form*
| > => result*
|
| I'd suggest using something more perspicuous than x, e.g. prefix:

I agree, prefix is much better now that the argument can't be an
integer.

| > Description:
| >
| > Executes a series of forms with each var bound to a fresh,
| > uninterned symbol. The uninterned symbol is as if returned by a
|
| uninterned symbol. The uninterned symbol is created as if by a
|
| Seems more readable, at least to my untrained eye...

To my untrained eye, too, so I'll use it.

| That said, you haven't specified if either the var or the prefix forms
| are evaluated. The var forms obviously aren't, but the prefix forms
| might be. If they aren't (like in your sample implementation), then
| the scope issue seems moot.

I didn't think about that; good you noticed. I can't think of a
single situation in which I would like the prefix form to be
evaluated, so I'll specify that it not be evaluated.

| This should probably be:
|
| Side Effects:
|
| Might increment *gensym-counter*.

Or maybe: "Might increment *GENSYM-COUNTER* once for each var."

| Might I suggest
|
| (defmacro with-unique-names ((&rest bindings) &body body)
| `(let ,(mapcar #'(lambda (binding)
| (destructuring-bind (var prefix)
| (if (consp binding) binding (list binding binding))
| `(,var (gensym ,(string prefix)))))
| bindings)
| ,@body))
|
| This delegates error checking to relevant system primitives, like
| destructuring-bind, string and let. This is good style, because
| implementations usually know best what is e.g. a valid string
| designator, or variable, _and_ we didn't specify any exceptional
| situations of our own, but the given example implementation might give
| the false impression that implementors should check for those specific
| errors.

I'm not sure I follow. An implementation generally implements a
superset of its specification; for instance, your version of W-U-N
may, in some Common Lisp implementations, accept prefix forms that are
not string designators. (See the CLHS entry for STRING.) Calling
W-U-N with a prefix form which is not a string designator is an error,
and may blow up if the W-U-N implementation is changed in the future.

To catch such errors, I think functions and macros that are in a
library---i.e, that are documented and meant to be used from other
modules and by other people---should signal an error when called in a
way that violates its specification _and_which_is_practical_to_detect_.

Checking the type of arguments that are not evaluated is practical, so
maybe the specification should contain something like:

WITH-UNIQUE-NAMES should signal TYPE-ERROR if it recives a var
which is not a symbol or a prefix which is not a string
designator.

--
Vebjorn Ljosa

Vebjorn Ljosa

unread,
Nov 23, 2001, 5:44:43 PM11/23/01
to
* Vebjorn Ljosa <ab...@ljosa.com>

|
| I'm not sure I follow. An implementation generally implements a
| superset of its specification; for instance, your version of W-U-N
| may, in some Common Lisp implementations, accept prefix forms that are
| not string designators. (See the CLHS entry for STRING.) Calling
| W-U-N with a prefix form which is not a string designator is an error,
| and may blow up if the W-U-N implementation is changed in the future.

After reading the definition of the term string designator more
closely, I now see that an implementation which extends STRING also
must extend the term string designator, so what I wrote above is not
correct.

| To catch such errors, I think functions and macros that are in a
| library---i.e, that are documented and meant to be used from other
| modules and by other people---should signal an error when called in a
| way that violates its specification _and_which_is_practical_to_detect_.
|
| Checking the type of arguments that are not evaluated is practical, so
| maybe the specification should contain something like:
|
| WITH-UNIQUE-NAMES should signal TYPE-ERROR if it recives a var
| which is not a symbol or a prefix which is not a string
| designator.

I still suggest that we add this to the W-U-N spec, though. But now I
see that the reference implementation can leave the type checking to
STRING.

--
Vebjorn Ljosa

Nils Goesche

unread,
Nov 23, 2001, 1:15:33 PM11/23/01
to
"Pierre R. Mai" <pm...@acm.org> writes:

> Nils Goesche <car...@cartan.de> writes:
>
> > Vebjorn Ljosa <ab...@ljosa.com> writes:
> >
> > > Here's a revised version. I didn't know about the name
> > > WITH-UNIQUE-NAMES, but now that Pekka brought it to my
> > > attention, I think it's a better name than WITH-GENSYMS, so I've
> > > changed the name in the specification.
> >

> > I prefer with-gensyms because it's clearer what's going on.
> > Sometimes it's hard to guess the name of Common Lisp functions and
> > macros; if you do (apropos 'gensym) you won't find
> > with-unique-names.
> >
> > And it's shorter :-)
>
> While that is true, I think that WITH-UNIQUE-NAMES and GENSYM don't
> have anything in common, except that one is implemented in terms of
> the other. I.e. you don't expect to find all list using functions
> when you use (apropos 'list) either.
>
> It is a statement to the need for a WITH-UNIQUE-NAMES that people
> think in terms of the much too low-level GENSYM when they want to
> create unique variable names. Decoupling these seems to me to be a
> good thing, even if it inconveniences old hands that are used to
> thinking in terms of GENSYM.

While that is true, too, I do not consider myself an ``old hand'' and
remember pretty well how I learned about macros: I read about them in
some introduction to Common Lisp. And the very first thing they teach
is how to use gensym with them. So, I could guess immediately what
with-gensyms was supposed to do when I first read about it (it was
even implemented somewhere, maybe in ``On Lisp''?).

OTOH, future text books could easily skip gensym and teach how to use
with-gensyms or with-unique-names, where the latter name would indeed
be a better choice.

But what the heck; I also strongly prefer `car' and `cdr' to `first'
and `rest' for some strange, unknown reason...

> The other reason is that the current specification of
> WITH-UNIQUE-NAMES is upwardly compatible with other specs for W-U-N
> that I've seen, so that would allow current usage of that macro to be
> grand-fathered in, without having to manually convert code bodies.

Sounds good. Although I've never encountered with-unique-names
before, personally...

Pierre R. Mai

unread,
Nov 23, 2001, 7:38:17 PM11/23/01
to
Vebjorn Ljosa <ab...@ljosa.com> writes:

> | This delegates error checking to relevant system primitives, like
> | destructuring-bind, string and let. This is good style, because
> | implementations usually know best what is e.g. a valid string
> | designator, or variable, _and_ we didn't specify any exceptional
> | situations of our own, but the given example implementation might give
> | the false impression that implementors should check for those specific
> | errors.
>
> I'm not sure I follow. An implementation generally implements a
> superset of its specification; for instance, your version of W-U-N
> may, in some Common Lisp implementations, accept prefix forms that are
> not string designators. (See the CLHS entry for STRING.) Calling
> W-U-N with a prefix form which is not a string designator is an error,
> and may blow up if the W-U-N implementation is changed in the future.

Someone writing portable code will not depend on such extensions.
Someone writing code that is acceptable for a certain implementation
might want to take advantage of the fact that it accepts something
else, everywhere it accpets the CL mandated string designators. Note
that the CL standard defines:

"string designator n. a designator for a string; that is, an object
that denotes a string and that is one of: a character (denoting a
singleton string that has the character as its only element), a
symbol (denoting the string that is its name), or a string (denoting
itself). The intent is that this term be consistent with the behavior
of string; implementations that extend string must extend the meaning
of this term in a compatible way."

So the function string canonically defines what an acceptable string
designator is in a certain implementation. Furthermore it is defined
to signal an appropriate error in all other cases. Second guessing
the implementations choices seems bad advice, especially if we don't
advertise that fact in the exceptional situations section. And we
shouldn't be more restrictive than the ANSI standard in such things.

Vebjorn Ljosa

unread,
Nov 24, 2001, 7:22:40 PM11/24/01
to
* Vebjorn Ljosa <ab...@ljosa.com>

| WITH-GENSYMS is one of those macros that are written over and over.
| Though there aren't that many variations---compared to SPLIT-SEQUENCE,
| EXTREMUM or the collection utilities---it might be good to nail down a
| specification and include it in Common Lisp Utilities.

I've revised the WITH-UNIQUE-NAMES spec again to take into account the
comments on the second draft. It is available on the Cliki:

http://ww.telent.net/cliki/with-unique-names

--
Vebjorn Ljosa

0 new messages