[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[smh: Re: [spr3808] Using symbols (instead of their print names) in defpackage]



This answer to a question posted on allegro-cl seems to have not
gotten on to allegro-cl.

Date: Fri, 2 Aug 91 05:11:34 PDT
From: smh (Steve Haflich)
Subject: Re: [spr3808] Using symbols (instead of their print names) in defpackage

[This matter has been assigned the tracking identifier "spr3808".
Please refer to it in any followup communication.]

Most of your problems and questions can be understood quite easily if
you remind yourself that, before each of your forms is EVALuated, the
form has first been READ in the current package by the reader.
Remember that the lisp top-level loop is approximately

      (loop (print (eval (read))))

and that READ makes side effects upon the package system by interning
symbols.  In what follows below I've interspersed some comments to
your message.  My comments are flush against the left margin, and my
code examples are indented twice the amount as your message.

   There seems to be a `feature' in ACL; consider the following form:

   (defpackage :x (:use :common-lisp) (:export a))

   I would expect the symbol with the print name "A" to be created in 
   the package named :x.  However, when this form is evaluated in a 
   prestine acl, looks what happens:
   ...

Before your defpackage form is evaluated it is first READ into the
current (USER) package.  This necessarily creates the internal symbols
USER::A and USER::B.

   <cl> (defpackage :x (:use :common-lisp) (:export a b))
----------------------------------------------------^-^

   #<The X package, 0 internal, 2 external>
   <cl> (find-symbol "A" :user)

   A
   :INTERNAL
   <cl> :pack :x

   <cl> (find-symbol "A" :user)

   COMMON-LISP-USER::A
   :INTERNAL

One way to have avoided the interning of USER::A would be to use
strings, keywords, or uninterned symbols in the DEFPACKAGE form, i.e.:

      (defpackage :x (:use :common-lisp) (:export :a :b))


   <cl> (find-symbol "A" :x)

   A
   :EXTERNAL

But note that sice the DEFPACKAGE extracted the print-name from
USER::A, the symbol interned in and then exported from the X package
is not the same symbol as USER::A.  In other words, the following is
_not_ now true:

      (eq 'x::a 'user::a)

   <cl> (use-package :x)
   Error: Using package `X' results in name conflicts for these symbols:
   (B A)

   Restart actions (select using :continue):
    0: unintern or shadow the conflicting symbols in the `COMMON-LISP-USER' package.

This is because package X exports symbol X::A but there is already a
different symbol "A" accessible in the USER package -- the USER::A you
created when your DEFPACKAGE form was READ into the user package.

   In CLtL2 pg. 271 under (:export {symbol-name}*), it states:
   Symbols with the specified names are located or created in the 
   package being defined and then exported, just as with the function 
   export. ...

That's right.  There was no symbol "A" accessible in the X package, so
one was created, and then exported as if by:

      (export (intern "A" (find-package "X")) (find-package "X"))

   On page 270 third paragraph, ... In every case, any option argument
   called package-name or symbol-name may be a string or a symbol; if 
   it is a symbol, only its print name matters, and not what package, 
   if any, the symbol happens to be in.

   The way I understand this is that a symbol with the print name "A" 
   should be created in the package named :x, then that symbol should 
   be exported.  It appears that acl is using the symbol and not it's 
   print name.  There appears to be a similar problem with the export 
   function as well.

I think my explanation above explains the error.

   <cl> :pack
   The COMMON-LISP-USER package is current.
   <cl> (export '(a1 b1) :x)
   Error: These symbols are not available in the X package:
      (A1 B1)

   Restart actions (select using :continue):
    0: Import these symbols into the `X' package.
   [1c] <cl>

   I would have expected, the symbols with print names "A1" and "B1" to 
   be created in the package named :x.  Is this problem pervasive 
   throughout the other package functions which also accept symbols or 
   their print-names?

Not quite.  The text you quoted from CLtL was:

      Symbols with the specified names are located or created in the 
      package being defined and then exported, just as with the function 
      export.

The final clause applies only to the "exported" part (since EXPORT
never creates any symbols).  In other words, what happens is
approximately:

      (export (intern NAME (find-package PACKAGE-NAME))
              (find-package PACKAGE-NAME))

The INTERN part finds or (if necessary) creates the symbol, then the
EXPORT part exports it.

   In general, what is the best way to refer to a symbol when defining 
   the package?  Given that Allegro provides various case sensitivity 
   modes, a symbol's print name is dependent upon the case sensitivity 
   mode (unless ones always uses a vertical bar as symbol delimiters).

The macroexpansion of a DEFPACKAGE form is instructive:

   user(2): :ma (defpackage :x (:use :common-lisp) (:export a))
   (eval-when (compile eval load)
     (excl::defpackage-1 ':x '((:use :common-lisp) (:export #:a))))

You will notice that DEFPACKAGE carefully extracts the SYMBOL-NAMEs of
all symbols according to spec.  It does this by "replacing" each
symbol with an uninterned symbol with the same name.  It would be
equivalent to use the SYMBOL-NAME strings instead of uninterned
symbols, except that (as you point out) Allegro implements
case-sensitivity extensions.  The fasl loader in Allegro provides
automatic case conversion of (uninterned) symbols to the appropriate
default case of the image doing the loading.  It does the right thing
for symbols, but can't (isn't authorized to) do anything about literal
string constants.

By the way, starting with release 4.0 Allegro implements the
non-portable Lisp Machine extension to reader syntax of allowing
package prefixes of compound expressions.  For instance, the following
are equivalent:

      (foo::a foo::b foo::c) == foo:(a b c)

and

      (:a :b :c) == :(a b c)

Therefore, here is the easiest (but non-portable) way to type your
original defpackage form, using symbols to achieve automatic
conversion between different case modes, and without interning
spurious symbols anywhere except the keyword package.  (Symbols
interned in the keyword package don't harm anything since the keyword
package neitehr uses nor is used by any other packages.)

      (defpackage :x :(use common-lisp) :(export a b))