From: Chris Capel
Subject: Sharp-colon reader macro
Date: 
Message-ID: <10kpri1j89qoj96@corp.supernews.com>
I was browsing the hyperspec and came across the #: reader macro.  I noticed
that this macro is used to display symbols created by GENSYM, and that lead
me to wonder why one needs to use GENSYM at all when writing macros.  Why
not something like

(defmacro fiddle (widget)
  (let ((var #:var))
    `(let ((widget* ,widget
...etc etc etc

instead of "(let ((var (gensym)))"?  The former certainly yields more
readable macroexpansions (the main benefit, I suppose), and it isn't any
worse to type.  It isn't any less friendly to a WITH-GENSYMS macro.

(defmacro with-gensyms (vars &body body)
  `(let ,(mapcar (lambda (var)
                    `(,var ,(make-symbol (symbol-name var))))
            vars)
     ,@body))

So is there a reason not to do this?

Chris Capel

From: Paul F. Dietz
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <SoKdnVPf2pO4b9HcRVn-og@dls.net>
Chris Capel wrote:
> I was browsing the hyperspec and came across the #: reader macro.  I noticed
> that this macro is used to display symbols created by GENSYM, and that lead
> me to wonder why one needs to use GENSYM at all when writing macros.  Why
> not something like
> 
> (defmacro fiddle (widget)
>   (let ((var #:var))
>     `(let ((widget* ,widget
> ...etc etc etc
> 
> instead of "(let ((var (gensym)))"?  The former certainly yields more
> readable macroexpansions (the main benefit, I suppose), and it isn't any
> worse to type.  It isn't any less friendly to a WITH-GENSYMS macro.

Because that gives you the same gensym-ed symbol each time you use
the macro, and if you have nested occurences of the macro you might
get name collisions.

	Paul
From: Vassil Nikolov
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <lz8yb629rl.fsf@janus.vassil.nikolov.names>
"Paul F. Dietz" <·····@dls.net> writes:

> Chris Capel wrote:
>> I was browsing the hyperspec and came across the #: reader macro.  I noticed
>> that this macro is used to display symbols created by GENSYM, and that lead
>> me to wonder why one needs to use GENSYM at all when writing macros.  Why
>> not something like
>> (defmacro fiddle (widget)
>>   (let ((var #:var))


  This is missing a quote, should have been (VAR '#:VAR).


>>     `(let ((widget* ,widget
>> ...etc etc etc
>> instead of "(let ((var (gensym)))"?  The former certainly yields more
>> readable macroexpansions (the main benefit, I suppose), and it isn't any
>> worse to type.  It isn't any less friendly to a WITH-GENSYMS macro.
>
> Because that gives you the same gensym-ed symbol each time you use
> the macro, and if you have nested occurences of the macro you might
> get name collisions.


  Besides (though less importantly), the different values from
  GENSYM's counter help figure out which occurrence of the symbol
  comes from which macro invocation.

  With an argument to GENSYM, e.g. (LET ((VAR (GENSYM "VAR-"))) ...),
  readability is preserved.


  ---Vassil.


-- 
Vassil Nikolov <········@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.
From: Alan Crowe
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <86vfeadze3.fsf@cawtech.freeserve.co.uk>
Paul wrote:
> Chris Capel wrote:
> > I was browsing the hyperspec and came across the #: reader macro.  I noticed
> > that this macro is used to display symbols created by GENSYM, and that lead
> > me to wonder why one needs to use GENSYM at all when writing macros.  Why
> > not something like
> > 
> > (defmacro fiddle (widget)
> >   (let ((var #:var))
> >     `(let ((widget* ,widget
> > ...etc etc etc
> > 
> > instead of "(let ((var (gensym)))"?  The former certainly yields more
> > readable macroexpansions (the main benefit, I suppose), and it isn't any
> > worse to type.  It isn't any less friendly to a WITH-GENSYMS macro.
>
> Because that gives you the same gensym-ed symbol each time you use
> the macro, and if you have nested occurences of the macro you might
> get name collisions.

From the hyperspec (my emphasis)

     2.4.8.5 Sharpsign Colon

     #: introduces an uninterned symbol whose name is
     symbol-name. Every time this syntax is encountered, a
     DISTINCT uninterned symbol is created.
		
so (eq '#:a '#:a) => NIL preventing name collisions.

The drawback that I can see is the duplication:

(let ((long-and-descriptive-name
       #:long-and-descriptive-name))

I've been playing with a replacement for with-gensyms

(defmacro with-syms((&rest symbol-list)&body code)
    `(let ,(mapcar (lambda(sym)
                     `(,sym (copy-symbol ',sym)))
                   symbol-list)
       ,@code))

For example

(defmacro repeat (count &body code)
    (with-syms (local-index-variable fixed-upper-limit)
      `(do ((,local-index-variable 0 (+ ,local-index-variable 1))
	    (,fixed-upper-limit ,count))
	   ((= ,local-index-variable ,fixed-upper-limit))
	   ,@code)))

* (repeat 2 (print 'even)(print 'odd))

EVEN 
ODD 
EVEN 
ODD 
NIL

* (macroexpand-1 '(repeat (random 4) (print 'even)(print 'odd)))

(DO ((#:LOCAL-INDEX-VARIABLE 0 (+ #:LOCAL-INDEX-VARIABLE 1))
     (#:FIXED-UPPER-LIMIT (RANDOM 4)))
    ((= #:LOCAL-INDEX-VARIABLE #:FIXED-UPPER-LIMIT))
  (PRINT 'EVEN)
  (PRINT 'ODD))
T

Notice that one loses the index numbers that GENSYM
gives. This looks like a win with macroexpand-1 because you
are only expanding one macro anyway. What happens with
macroexpand and macroexpand-all? Without the index numbers
one might not be able to debug nested macros. I lack the
experience to know if the loss here outweighs the gain from
avoiding the clutter of unwanted numbers when using
macroexpand-1.

Alan Crowe
Edinburgh
Scotland
From: Pascal Bourguignon
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <87sm9ekzp8.fsf@thalassa.informatimago.com>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> so (eq '#:a '#:a) => NIL preventing name collisions.

You missed the important fact of this discussion:

(defun a () '#:a)
(eq (a) (a)) => T !!!

Agreed, for a lexical binding it would make a difference, but if you
use that symbol for something else, you would have colisions as soon
as you use the macro twice, and the more probably lethally so if it
can be used recursively:

    (m (:attr 1)
         (print 
            (m (:attr 2)
                 (do-something))))

Should I spell a example for m?

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.
From: Alan Crowe
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <86zn3lt4m2.fsf@cawtech.freeserve.co.uk>
Pascal Bourguignon wrote:
> Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> > so (eq '#:a '#:a) => NIL preventing name collisions.
>
> You missed the important fact of this discussion:
>
> (defun a () '#:a)
> (eq (a) (a)) => T !!!

Oh no! I got my "times when things happen" muddled.
#:a generates distinct symbols at read time. I would have to
re-read my (defmacro ... ) from the source file to get a
distinct symbol, which isn't any use at all for writing
macros. 

(defun a () '#:a)
(eq (a) (progn (eval '(defun a () '#:a)) (a))) => NIL

This is much more subtle than I realised:

(defmacro repeat (count &body code)
    ;; code that skates on thin ice,
    ;; alone, in the dark.
    (let ((i '#:i)
	  (n '#:n))
      `(do ((,i 0 (+ ,i 1))
	    (,n ,count))
	   ((= ,i ,n))
	   ,@code)))

(repeat 2 (repeat 2 (write '*)))
=> ****
   NIL

I avoid capturing any variables from programmer written
code, due to using #: (the bit I understood)

I avoid the nested calls of repeat standing on each others
toes. I incorrectly thought I was getting distinct symbols,
because I missed the point that read time is all over before
macroexpansion time comes. But I get lucky and lexical
scoping saves me with distinct, nested bindings of the same
symbol. 

> Should I spell a example for m?

Yes please. I've only ever written macros in which I would
be rescued by lexical scoping. Something fancier would
expand my horizons.

Alan Crowe
Edinburgh
Scotland
From: Pascal Bourguignon
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <87zn3kirb2.fsf@thalassa.informatimago.com>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> Pascal Bourguignon wrote:
> > Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> > > so (eq '#:a '#:a) => NIL preventing name collisions.
> >
> > You missed the important fact of this discussion:
> >
> > (defun a () '#:a)
> > (eq (a) (a)) => T !!!
> 
> Oh no! I got my "times when things happen" muddled.
> #:a generates distinct symbols at read time. I would have to
> re-read my (defmacro ... ) from the source file to get a
> distinct symbol, which isn't any use at all for writing
> macros. 
> 
> (defun a () '#:a)
> (eq (a) (progn (eval '(defun a () '#:a)) (a))) => NIL
> 
> This is much more subtle than I realised:
> 
> (defmacro repeat (count &body code)
>     ;; code that skates on thin ice,
>     ;; alone, in the dark.
>     (let ((i '#:i)
> 	  (n '#:n))
>       `(do ((,i 0 (+ ,i 1))
> 	    (,n ,count))
> 	   ((= ,i ,n))
> 	   ,@code)))
> 
> (repeat 2 (repeat 2 (write '*)))
> => ****
>    NIL
> 
> I avoid capturing any variables from programmer written
> code, due to using #: (the bit I understood)
> 
> I avoid the nested calls of repeat standing on each others
> toes. I incorrectly thought I was getting distinct symbols,
> because I missed the point that read time is all over before
> macroexpansion time comes. But I get lucky and lexical
> scoping saves me with distinct, nested bindings of the same
> symbol. 
> 
> > Should I spell a example for m?
> 
> Yes please. I've only ever written macros in which I would
> be rescued by lexical scoping. Something fancier would
> expand my horizons.

Now you should see when you can get problems: when you use the
anonymous symbol for something else than a lexical variable.  Anything
that modifies a global state, be it the current package or for example
a hash table where you store the anonymous symbol with some other
data, for reference by other code generated by your macro.

For example:
          
(defmacro defcommand (pattern &body body)
    (let ((name #:command))
      `(progn
         (defun ,name (args) ,@body)
         (push (cons ',pattern ',name) *commands*))))

(defmacro parse-command (command)
    `(match-case ,command
       ,@(mapcar (lambda (p-f)
                     `(,(car p-f) (,(cdr-pf) ,(collect-vars ,(car p-f)))))
                 *commands*)))

(defcommand (take (?x object))
   (detach object)
   (attach object (bag *player*)))

(defcommand (throw (?x object))
    (cond ((containp (bag player) object)
                (detach object)
                (attach object *ground*))
          (t (error "You don't hold ~A" object))))

(loop for command = (read) do
     (parse-command command))
      

Both invocations of defcommand would define the same function,
(ie. all but the first would redefine the function), and only the
first command could be run.  It would work if you replaced #:command
by (gensym "COMMAND").


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.
From: Alan Crowe
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <86fz5bberl.fsf@cawtech.freeserve.co.uk>
Pascal Bourguignon wrote:
> Now you should see when you can get problems: when you use the
> anonymous symbol for something else than a lexical variable.  Anything
> that modifies a global state, be it the current package or for example
> a hash table where you store the anonymous symbol with some other
> data, for reference by other code generated by your macro.
>
> For example:
>
> (defmacro defcommand (pattern &body body)
>     (let ((name #:command))
>       `(progn
>	  (defun ,name (args) ,@body)
>	  (push (cons ',pattern ',name) *commands*))))
>
> (defmacro parse-command (command)
>     `(match-case ,command
>	,@(mapcar (lambda (p-f)
>		      `(,(car p-f) (,(cdr-pf) ,(collect-vars ,(car p-f)))))
>		  *commands*)))
>
> (defcommand (take (?x object))
>    (detach object)
>    (attach object (bag *player*)))
>
> (defcommand (throw (?x object))
>     (cond ((containp (bag player) object)
>		 (detach object)
>		 (attach object *ground*))
>	   (t (error "You don't hold ~A" object))))
>
> (loop for command = (read) do
>      (parse-command command))

Thankyou Pascal, that is a tremendous example. I'm
stunned. My own efforts to contrive a need for GENSYM rather
than Sharpsign Colon started with special variables
(excessive use of special variables is the royal road to
toxic symbol clashes, I think). Err, then I got stuck,
without a credible scenario.

I'm especially impressed because I had toyed with this kind
of adventure code quite recently. How should parse command
work? I was doing (apply (car command)(cdr command)). I
avoided clashes between my commands and functions in the
program by having a command package. The functions that
implemented the commands started

(defun command:take (&rest stuff) etc etc)

I was well pleased with this code, so to have your example
code show me, in passing, a better way has expanded my
horizons wonderfully. I've spent a few hours writing a
minimal pattern matcher and filling in the details so I can
play with running code and soak up the implications.  Now I
know why GENSYM clutters up my macroexpansions with those
irritating numbers. When I look in *COMMANDS* I really want
to see:

(((HIT MONSTER WITH (X)) . #:HIT-971)
 ((DROP (X) ON MONSTER) . #:DROP-970)
 ((THROW ROCK AT MONSTER) . #:THROW-969)
 ((THROW (X) AT (Y)) . #:THROW-968))

with numbers to tell me that the THROW symbols are distinct
and it is all working right.

Having powerful macros to type all the boilerplate for you
makes programming fun

* (let ((*readtable* (copy-readtable)))
    (set-macro-character #\newline (get-macro-character #\) ))
    (loop for command = (progn
	  		  (format t "~&> ")
			  (read-delimited-list #\newline)) 
          do (parse-command command)))

> hit monster with sword
The monster snarls at you.
> drop anvil on monster
You got a step ladder to stand on?
> hit monster with rock
The monster snarls at you.
> throw rock at monster
The monster picks you up by the scruff of the neck
and drops you into the debugger!

Error in function "Top-Level Form":  Broken fourth wall.

Restarts:
  0: [CONTINUE] Return to the dungeon
  1: [ABORT   ] Return to Top-Level.


Alan Crowe
Edinburgh
Scotland
From: Pascal Bourguignon
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <87llf3gvt5.fsf@thalassa.informatimago.com>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> I'm especially impressed because I had toyed with this kind
> of adventure code quite recently. How should parse command
> work? I was doing (apply (car command)(cdr command)). I
> avoided clashes between my commands and functions in the
> program by having a command package. The functions that
> implemented the commands started

A first, quick-and-dirty implementation could be to use a pattern
matched.  You could try Fare's pattern matcher, or mine ( temporarily
at: http://thalassa.informatimago.com/local/pmatch.lisp ) which may be
simplier and easier to understand.  It's not very efficient since each
pattern is tried sequentially.  I'm going to rewrite it using a DFA,
as if a regexp. Note the match-case macro.
 

> I was well pleased with this code, so to have your example
> code show me, in passing, a better way has expanded my
> horizons wonderfully. I've spent a few hours writing a
> minimal pattern matcher and filling in the details so I can
> play with running code and soak up the implications.  Now I
> know why GENSYM clutters up my macroexpansions with those
> irritating numbers. When I look in *COMMANDS* I really want
> to see:
> 
> (((HIT MONSTER WITH (X)) . #:HIT-971)
>  ((DROP (X) ON MONSTER) . #:DROP-970)
>  ((THROW ROCK AT MONSTER) . #:THROW-969)
>  ((THROW (X) AT (Y)) . #:THROW-968))
> 
> with numbers to tell me that the THROW symbols are distinct
> and it is all working right.
> 
> Having powerful macros to type all the boilerplate for you
> makes programming fun

Indeed.
 
> * (let ((*readtable* (copy-readtable)))
>     (set-macro-character #\newline (get-macro-character #\) ))
>     (loop for command = (progn
> 	  		  (format t "~&> ")
> 			  (read-delimited-list #\newline)) 
>           do (parse-command command)))
> 
> > hit monster with sword
> The monster snarls at you.
> > drop anvil on monster
> You got a step ladder to stand on?
> > hit monster with rock
> The monster snarls at you.
> > throw rock at monster
> The monster picks you up by the scruff of the neck
> and drops you into the debugger!
> 
> Error in function "Top-Level Form":  Broken fourth wall.
> 
> Restarts:
>   0: [CONTINUE] Return to the dungeon
>   1: [ABORT   ] Return to Top-Level.

:-)

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.
From: Vassil Nikolov
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <lzhdpsz2lf.fsf@janus.vassil.nikolov.names>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> [...]
> lexical scoping saves me with distinct, nested bindings of the same
> symbol.


  Not always.

  Consider the following (simplistic) example of providing read-only
  access to the iteration counter:

    (defmacro repeat ((counter-reader-name count-form) &body body)
      (let ((counter-var (gensym)))
        `(macrolet ((,counter-reader-name () '(identity ,counter-var)))
           (dotimes (,counter-var ,count-form) ,@body))))

  and the following use:

    (repeat (i 2) (repeat (j 2) (format t "~%~D ~D" (i) (j))))

  and then the result from replacing the call to GENSYM with
  '#:COUNTER.


  ---Vassil.


-- 
Vassil Nikolov <········@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.
From: Adam Warner
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <pan.2004.09.19.05.10.11.515358@consulting.net.nz>
Hi Chris Capel,

> The former certainly yields more readable macroexpansions

This is the WITH-GENSYMS macro I use (and wrote):

  (defmacro with-gensyms ((&rest symbols) &body code)
    `(let (,@(loop for sym in symbols
                   collect `(,sym (gensym ,(symbol-name sym)))))
      ,@code))

It supplies the optional string argument to GENSYM based upon the symbol
name you are using for substitution. Generated symbols become just as
intelligible as regular symbols with zero extra work.

Regards,
Adam
From: Chris Capel
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <10krbsu5ala5ea9@corp.supernews.com>
Adam Warner wrote:

> Hi Chris Capel,
> 
>> The former certainly yields more readable macroexpansions
> 
> This is the WITH-GENSYMS macro I use (and wrote):
> 
>   (defmacro with-gensyms ((&rest symbols) &body code)
>     `(let (,@(loop for sym in symbols
>                    collect `(,sym (gensym ,(symbol-name sym)))))
>       ,@code))
> 
> It supplies the optional string argument to GENSYM based upon the symbol
> name you are using for substitution. Generated symbols become just as
> intelligible as regular symbols with zero extra work.

Thanks! I already knew about the argument to GENSYM. The only reason to use
the reader macro (if it worked) would be that it's easier to type. And
easier to read too. I really do think that #:MYVAR is easier to read in a
macroexpansion than #:MYVAR8435, don't you? Ohhh, I have an idea!

(let ((counter 0))
  (defmacro with-gensyms (vars &body body)
    `(let ,(mapcar (lambda (var)
                     `(,var (make-symbol
                             ,(format nil "_~A_~A"
                                      (string-downcase (symbol-name var))
                                      (incf counter)))))
                   vars)
       ,@body)))


!! That's gives much more readable macroexpansions! Now instead of
#:MYVAR8435, we get #:_myvar_15. Now that's an improvement.

Chris Capel
From: Vassil Nikolov
Subject: Re: Sharp-colon reader macro
Date: 
Message-ID: <lz3c1d21kt.fsf@janus.vassil.nikolov.names>
Chris Capel <······@iba.nktech.net> writes:

> [...]
> (let ((counter 0))
>   (defmacro with-gensyms (vars &body body)
>     `(let ,(mapcar (lambda (var)
>                      `(,var (make-symbol
>                              ,(format nil "_~A_~A"
>                                       (string-downcase (symbol-name var))
>                                       (incf counter)))))
>                    vars)
>        ,@body)))


  A non-top-level DEFMACRO form is usually undesirable.  (For example,
  the macro definition won't be available in the rest of the file.)

  Also, don't you want to bind *GENSYM-COUNTER* instead, so as not to
  duplicate what GENSYM can do?


> !! That's gives much more readable macroexpansions! Now instead of
> #:MYVAR8435, we get #:_myvar_15. Now that's an improvement.


  By the way, that would typically be printed as #:|_myvar_15| (so one
  may consider changing *PRINT-CASE*, rather than downcasing symbol
  names).  Also, I am not sure that a leading underscore is good for
  readability.


  ---Vassil.


-- 
Vassil Nikolov <········@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.