common-lispreader-macro

Differences between Sharpsign Colon and Gensym


I've just been reading up on the sharpsign colon reader macro and it sounded like it had a very similar effect to gensym

Sharpsign Colon: "introduces an uninterned symbol"

Gensym: "Creates and returns a fresh, uninterned symbol"

So a simple test

CL-USER> #:dave
; Evaluation aborted on #<UNBOUND-VARIABLE DAVE {1002FF77D3}>.
CL-USER> (defparameter #:dave 1)
#:DAVE
CL-USER> #:dave
; Evaluation aborted on #<UNBOUND-VARIABLE DAVE {100324B493}>.

Cool so that fails as it should.

Now for the macro test

(defmacro test (x)
  (let ((blah '#:jim))
    `(let ((,blah ,x))
       (print ,blah))))

CL-USER> (test 10)

10 
10
CL-USER>

Sweet so it can be used like in a gensym kind of way.

To me this looks cleaner than gensym with an apparently identical result. I'm sure I'm missing a vital detail so my question is, What it it?


Solution

  • GENSYM is like MAKE-SYMBOL. The difference is that GENSYM supports fancy naming by counting up -> thus symbols kind of have unique names, which makes debugging a bit easier when having gensyms for example in macro expansions.

    #:foo is a notation for the reader.

    So you have a function which creates these and a literal notation. Note that, when *print-circle* is true, some kind of identity maybe preserved in s-expressions: #(#1=#:FOO #1#).

    Generally this is similar to (a . b) and (cons 'a 'b), #(a b) and (vector 'a 'b)... One is literal data and the other one is a form which will create ('cons') fresh objects.

    If you look at your macro, the main problem is that nested usage of it could cause problems. Both lexically or dynamically.

    Using a generated symbol at macro expansion time would make sure that different and expanded code would not share bindings.