Ok, based on replies to my earlier post about colons and
syntax, here's an extension:


So what's up with the #:uninterned symbols?

The special syntax sharp-colon (#:) creates an "uninterned
symbol."  This is a symbol with no package at all, and is
different from both normal symbols and keyword symbols.
A new uninterned symbol is created by the reader every time
you use it.

Uninterned symbols are not keywords!  They follow the same
evaluation rules as normal symbols.  So if you type :FOO at
the REPL prompt, it evaluates to itself and you get :FOO
back.  But if you type #:FOO, the interpreter tries to
evaluate the newly-created symbol, which doesn't have a
value (because it was just created) so you get an error.

Uninterned symbols are used to prevent name conflicts in the
future.  For example, the following code (thanks to Pascal
Bourguignon) produces an error:

    (in-package :cl-user)

    (defpackage mypackage
      (:use :common-lisp)
      (:export foo bar))

    (use-package 'mypackage)

To understand why, we need to look at what is happening on
each line.  The first line sets the current package to
CL-USER.  Then the second line defines a new package,
MYPACKAGE, with two exported symbols, FOO and BAR.  But when
the Lisp reader encounters FOO and BAR, it reads them in the
context of the *current* package, which is still CL-USER.
So, the reader interns two new symbols, CL-USER::FOO and
CL-USER::BAR.

On the last line, (USE-PACKAGE 'MYPACKAGE) tries to import
MYPACKAGE:FOO and MYPACKAGE:BAR into CL-USER, but we've
already defined CL-USER::FOO and CL-USER::BAR!  The
interpreter doesn't know which FOO and BAR are the right
ones, so we get a "name conflict."

To prevent this kind of error, we can use uninterned
symbols:

    (defpackage #:mypackage
      (:use #:common-lisp)
      (:export #:foo #:bar))

But in fact, DEFPACKAGE (and many other commonplace macros)
only cares about the *name* of the symbol, not the symbol
itself.  The CLHS calls these arguments "symbol
designators."  Symbol designators can be uninterned symbols,
as above, *or* keyword symbols, *or* strings.  So either of
the following will also work:

    (defpackage :mypackage
      (:use :common-lisp)
      (:export :foo :bar))

    (defpackage "MYPACKAGE"   ; remember symbol names get
      (:use "COMMON-LISP")    ; converted to upper case
      (:export "FOO" "BAR"))  ; by the Lisp reader

So which one should you use?  As discussion here showed,
there's no simple answer.  Using keywords wastes memory,
since all those symbols get interned in the KEYWORD package
and then never used again.  Using uninterned symbols is
slightly inefficient, since a new symbol has to be created
each time you use it.  Using strings makes your code
non-portable to Lisps that do *not* convert symbol names to
upper case, such as Allegro CL in "modern" mode.

As Peter Seibel pointed out, it may not really matter, since
the compiler may convert whatever form you use into strings.

The most common style seems to be to use keywords for
package names and uninterned symbols for exports:

    (defpackage :mypackage
      (:use :common-lisp)
      (:export #:foo #:bar))

This makes sense given that you'll probably need to refer to
the package name again in a USE-PACKAGE somewhere, so you
might as well leave the symbol interned (defined) in the
KEYWORD package.  You probably won't be using :FOO and :BAR
again as keywords, so there's no need to keep them hanging
around in the KEYWORD package.

The bottom line?  Uninterned symbols are useful for places
where you need to refer to a symbol by name before you
actually define it.  Unlike keywords, they don't hang around
for the rest of your program, and they are a bit more
portable than strings.

-Stuart
_______________________________________________
Gardeners mailing list
[email protected]
http://www.lispniks.com/mailman/listinfo/gardeners

Reply via email to