Up to external symbols.
This commit is contained in:
parent
c0a5a2b7da
commit
c6d958e6a0
@ -613,25 +613,21 @@
|
||||
From now on, the default package is ="P1"=. If we write down a symbol then it
|
||||
will be understood by the Lisp reader as accessed via the package ="P1"=,
|
||||
that is, unless we use a special syntax. Let us create a list of three
|
||||
symbols and assign it to the symbol =list=, and then examine the internal
|
||||
symbols and assign it to the symbol =my-list=, and then examine the internal
|
||||
symbols of the package ="P1"=:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1"
|
||||
(multiple-value-list
|
||||
(ignore-errors
|
||||
(setf (symbol-value 'list) '(symb1 symb2 symb3))))
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(setf (symbol-value 'my-list) '(symb1 symb2 symb3))
|
||||
#+END_SRC
|
||||
|
||||
The reader probably would prefer =(defvar list)= followed by
|
||||
=(setf list ...)=, which is rejected by some Lisp implementations, in
|
||||
particular SBCL, because in principle the symbol =list=, owned by the package
|
||||
="COMMON-LISP"=, is "locked". It is possible to unlock such a symbol, which
|
||||
is a little technical, but using =(symbol-value 'list)= as above gets the job
|
||||
done.
|
||||
="COMMON-LISP"=, is "locked". We use the symbol =my-list= instead.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1"
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(multiple-value-list
|
||||
(ignore-errors
|
||||
(ignore-errors
|
||||
(package-internal-symbols "P1")))
|
||||
#+END_SRC
|
||||
|
||||
@ -656,7 +652,7 @@
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1"
|
||||
(eq (find-symbol "PACKAGE-INTERNAL-SYMBOLS" "COMMON-LISP-USER")
|
||||
(find-symbol "PACKAGE-INTERNAL-SYMBOLS" "P1"))
|
||||
(find-symbol "PACKAGE-INTERNAL-SYMBOLS" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
The symbols are different. Compare that with the status of =nil=, where the
|
||||
@ -756,3 +752,371 @@
|
||||
(package-internal-symbols "P1")
|
||||
#+END_SRC
|
||||
|
||||
The Lisp terminology is rather misleading: in fact, it is not at all the
|
||||
symbol which is imported, but the connection /string/ $\mapsto$ /symbol/.
|
||||
More precisely, let $p_1$ and $p_2$ be two packages, $st$ a string and $sm$
|
||||
a symbol. If $\rho(p_1)(st) = sm$ holds before the importation, then, after
|
||||
the importation, both relations
|
||||
$\rho(p_1)(st) = sm$ /and/ $\rho(p_2)(st) = sm$ are satisfied. In particular,
|
||||
the first relation remains valid. It is not really an importation; it is a
|
||||
/copy/ in the package $p_2$ of the connection $st \mapsto sm$, available in
|
||||
the package $p_1$.
|
||||
|
||||
Furthermore, nothing is modified about the symbol itself, except /possibly/,
|
||||
but rarely, its home package, as will be examined later. It is tempting for a
|
||||
beginner to think the home-package of the symbol is being modified. Not at
|
||||
all! Another erroneous interpretation would be to think a /copy/ of the
|
||||
symbol to be imported is made "inside" the target package, with all the
|
||||
corresponding ingredients of the symbol, value, functional value, and so on.
|
||||
Wrong again! A copy is made, but only of a /pointer/ toward our symbol found
|
||||
in $InS(p_1)$ and copied in $InS(p_2)$.
|
||||
|
||||
The [[http://clhs.lisp.se/Body/f_import.htm][=import=]] function has two arguments, the /symbol/ to be imported and the
|
||||
/target package/. The symbol must be well defined, that is, taking account of
|
||||
the current state of the environment. Here, before the import, the symbol is
|
||||
accessible only via ="CL-USER"=, and it is mandatory to use the notation
|
||||
=cl-user::...= (and the symbol must also be quoted to preclude its
|
||||
evaluation). After the import, as can be seen in the example, the symbol can
|
||||
be used functionally from the package ="P1"= without the prefix notation.
|
||||
Although the imported symbol is in the list of the internal symbols of the
|
||||
package ="P1"=, it is not quite the same as before. The next mandatory
|
||||
exercise consists in comparing the original symbol and the imported one.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eq 'cl-user::package-internal-symbols
|
||||
'package-internal-symbols)
|
||||
#+END_SRC
|
||||
|
||||
The function =eq= proves that both notations name the same symbol. Here's
|
||||
another proof:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eq (find-symbol "PACKAGE-INTERNAL-SYMBOLS" "CL-USER")
|
||||
(find-symbol "PACKAGE-INTERNAL-SYMBOLS" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
If you remember the =symbol-package= function, the next test is revealing:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-package (find-symbol "PACKAGE-INTERNAL-SYMBOLS" "CL-USER"))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-package (find-symbol "PACKAGE-INTERNAL-SYMBOLS" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
The /unique/ symbol accessed by two different paths has of course a unique
|
||||
home package, namely its /birth place/, the ="CL-USER"= package. Please, do
|
||||
not pay too much attention to this symbol package; it is just an artifact of
|
||||
the session's history and does not have any real interest. You must focus on
|
||||
the chain
|
||||
$\mathit{package}\rightarrow[\mathit{string}\rightarrow \mathit{symbol}]$ and
|
||||
not much else. There is no real reason to be concerned about the map
|
||||
=symbol-package=: $\mathit{symbol}\rightarrow\mathit{package}$.
|
||||
|
||||
* Name conflicts.
|
||||
|
||||
If poorly understood, the introduction of Lisp packages to a Lisp project
|
||||
will sooner or later lead to /name conflicts/. There is nothing mysterious
|
||||
about them and there are standard "home remedies" for dealing with them.\\
|
||||
|
||||
*Warning.* A name conflict can only be generated if two /different/ symbols
|
||||
$sm$, $sm' \in Sm$ end up with the same name: $sn(sm) = sn(sm')$. A name
|
||||
conflict occurs if these symbols $sm$ and $sm'$ become accessible via the
|
||||
same package $p$, which is a forbidden situation, since it would introduce
|
||||
ambiguity to the =find-symbol= function. On the other hand, the fact that
|
||||
some symbol (singular) is accessible via /different/ packages never generates
|
||||
a name conflict by itself.
|
||||
|
||||
Lisp constantly is on the lookout for possible name conflicts and stops with
|
||||
an error if the evaluation of an expression causes a name conflict. Some
|
||||
errors are /continuable/, which means that Lisp is able to propose a conflict
|
||||
resolution via =unintern= or =shadowing-import=. Remember the use of
|
||||
=unintern= in the previous section?
|
||||
|
||||
What happens if an imported symbol has the same /name/ as a symbol already
|
||||
accessible in the target package? In mathematical language, let $p_1$ and
|
||||
$p_2$ be two different packages, $st$ some string, and $sm_1$ and $sm_2$ two
|
||||
different symbols, internal in the respective packages $p_1$ and $p_2$, with
|
||||
same name $st$. In other words, assume $\rho(p_1)(st) = sm_1$ and
|
||||
$\rho(p_2)(st) = sm_2$, $sm_1 \in InS(p_1)$ and $sm_2 \in InS(p_2)$, and
|
||||
$sm_1\neq sm_2$. A situation of this sort could be the following (Remember
|
||||
the current default package is ="P1"=!):
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(defvar cl-user::symb4)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(setf cl-user::symb4 111)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(defvar symb4)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(setf symb4 111)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eq 'cl-user::symb4 'symb4)
|
||||
#+END_SRC
|
||||
|
||||
The evaluation of the first expression interns a symbol of name ="SYMB4"= in
|
||||
the package ="CL-USER"= and gives it the value 111, while the second
|
||||
expression interns a /different/ symbol, again of name ="SYMB4"=, in the
|
||||
package ="P1"=, the current default package, and also gives it the value 111.
|
||||
As a result two symbols have been allocated, installed somewhere in the
|
||||
environment, with the same value of 111. Also the connections
|
||||
$\rho(\mathtt{"CL-USER"}): \mathtt{"SYMB4"}\mapsto \mathtt{cl-user::symb4}$
|
||||
and $\rho(\mathtt{"P1"}): \mathtt{"SYMB4"}\mapsto\mathtt{p1::symb4}$ have
|
||||
been installed, that is, the addresses of both symbols have been pushed to
|
||||
the /respective/ lists of /internal/ symbols of the packages ="CL-USER"= and
|
||||
="P1"=. The =eq= comparison confirms that the symbols just allocated are
|
||||
different. Two symbols with the same name ="SYMB4"= are now present in our
|
||||
environment. This is possible because these symbols are interned in different
|
||||
packages. Each one is accessible via the appropriate package. Both symbols
|
||||
are live and can be freely used:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(setf cl-user::symb4 (+ symb4 cl-user::symb4))
|
||||
#+END_SRC
|
||||
|
||||
What happens if we import =cl-user::symb4= into the package ="P1"=? Let's
|
||||
try!
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(multiple-value-list
|
||||
(ignore-errors
|
||||
(import 'cl-user::symb4 "P1")))
|
||||
#+END_SRC
|
||||
|
||||
The importation is rejected, for it would not be possible anymore to
|
||||
coherently define $\rho(\mathtt{"P1"})(\mathtt{"SYMB4"})$. We will see later
|
||||
that another process called /shadowing/ allows the user to suppress the
|
||||
symbol =symb4= already present in ="P1"= and to authorize the importation of
|
||||
the symbol present in ="CL-USER"=.
|
||||
|
||||
* The function =intern=.
|
||||
|
||||
**Fact 3:** Let $p$ be a package and $(sm_1,...,sm_n)$ the list of its
|
||||
internal symbols. If $p$ is the default package, any of these symbols can be
|
||||
directly used without the double colon notation ='::'=. If $p$ is not the
|
||||
default package, these symbols can be used thanks to the notation $p::sm_i$.
|
||||
All these internal symbols have different names. Nothing prevents several
|
||||
packages from having the same symbols in their lists of internal symbols.
|
||||
This is possible thanks to the =import= function which can work only if no
|
||||
name conflict is generated.\\
|
||||
|
||||
To be complete on this matter, we now to consider the Lisp function
|
||||
[[http://clhs.lisp.se/Body/f_intern.htm][=intern=]]. It is used almost exactly as the function =find-symbol= with just a
|
||||
difference: if no symbol is found, then a symbol is allocated and interned in
|
||||
the package referred to in its second argument. Compare the following:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(multiple-value-list (find-symbol "SYMB5" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(multiple-value-list (intern "SYMB5" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(multiple-value-list (intern "SYMB5" "P1"))
|
||||
#+END_SRC
|
||||
|
||||
We start without any =symb5= symbol in ="P1"=, which is confirmed by the
|
||||
double negative answer of =find-symbol=. If instead we use the function
|
||||
=intern=, Lisp notices that such a symbol is absent from ="P1"= and therefore
|
||||
allocates a symbol of name ="SYMB5"= and /interns/ it in the package ="P1"=
|
||||
as an internal symbol. The second value =nil= is an indicator of the
|
||||
allocation just made. If we repeat the same expression, which is not an
|
||||
error, then Lisp signals that such a symbol is already present and returns
|
||||
its status, which also informs us of its previous presence. A little weird!
|
||||
This definition of the function =intern= guarantees the coherence of the set
|
||||
of internal symbols of packages. A symbol can be allocated only through this
|
||||
function =intern=. It requires two arguments, a string $st$ and a package
|
||||
$p$. In a sense, the function =intern= first calls =(find-symbol st p)= with
|
||||
the same arguments. If a symbol is found it is returned without modification
|
||||
and no allocation of a new symbol takes place, which otherwise would create
|
||||
a name conflict. On the other hand, if a corresponding symbol is not found,
|
||||
it is allocated and interned in the package explicitly or implicitly quoted,
|
||||
and this new symbol certainly does not generate a name conflict. In this way,
|
||||
the uniqueness of the names of internal symbols of a package is enforced.
|
||||
This does not prevent two different symbols of the environment from having
|
||||
the same name, but the packaging system is organized such that they cannot be
|
||||
accessed via the same package.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(package-internal-symbols "P1")
|
||||
#+END_SRC
|
||||
|
||||
In practice, the Lisp reader constantly uses the =intern= function, for
|
||||
example when reading an expression. If it detects the presence of a symbol,
|
||||
the colon notation allows it to decide if a package is explicitly given,
|
||||
otherwise it must use the default package. The name of the symbol, alone or
|
||||
package-qualified using the colon notation, then unambiguously determines the
|
||||
symbol. If present, it is used, otherwise it is allocated and then used. This
|
||||
is exactly the role of the =intern= function.
|
||||
|
||||
* Moving a symbol between two packages.
|
||||
|
||||
Up until now, we have seen no means of modifying the home package of a
|
||||
symbol. In fact, it is possible but may seem a little contorted. It is almost
|
||||
without any practical interest except as an opportunity to use this as a
|
||||
vehicle to discover other facts. First, it is possible to /unintern/ a
|
||||
symbol. For example, we can unintern the symbol ="SYMB1"= from the package
|
||||
="P1"=. What happens is precisely that this symbol is removed from the list
|
||||
of the internal symbols of ="P1"=.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(unintern 'symb1 "P1")
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(package-internal-symbols "P1")
|
||||
#+END_SRC
|
||||
|
||||
What is modified is the /list/ of internal symbols in the package ="P1"=. The
|
||||
symbol =symb1= itself has not been modified at all, except possibly its home
|
||||
package. In particular the symbol is not deallocated or destroyed, and
|
||||
therefore always present somewhere in the environment.
|
||||
|
||||
But it's become inaccessible as such, for a symbol most often is accessible
|
||||
only via a package, the default one or some other explicitly given. The
|
||||
symbol =symb1= was interned only in the package ="P1"=, so that it appears
|
||||
now to be unreachable! If a symbol is truly unreachable, the Lisp garbage
|
||||
collector, always silently working in the background, will establish that
|
||||
fact and free the corresponding part of memory for reuse. Except that our
|
||||
symbol remains reachable, for it was the first element of the list assigned
|
||||
to the symbol =my-list=. Let's have a look at its value!
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
my-list
|
||||
#+END_SRC
|
||||
|
||||
Our symbol is still present in the list but in a little different form.
|
||||
To be sure, it is still a symbol:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(type-of (first my-list))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-package (first my-list))
|
||||
#+END_SRC
|
||||
|
||||
No home package, i.e., our symbol is "homeless"! The prefix ='#:'= indicates
|
||||
that this symbol is homeless. The =unintern= function had this effect because
|
||||
the package argument of =unintern= was the package which /owned/ the symbol.
|
||||
Otherwise the home package would not be modified. Although our symbol has
|
||||
become homeless, it continues to be an ordinary symbol, for example, we can
|
||||
assign it a value:
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(set (first my-list) 444)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-value (first my-list))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eval (first my-list))
|
||||
#+END_SRC
|
||||
|
||||
Unlike =setf= (macro), the =set= function evaluates its arguments, so that
|
||||
before the assignment, the expression =(first my-list)= is evaluated, which
|
||||
returns our mysterious symbol =#:symb1=. Once that's done, the =set= function
|
||||
assigns (the value of) the second argument to it, as is confirmed by the
|
||||
other expressions. It is even possible to /import/ a homeless symbol in a
|
||||
package, but then the target package becomes the home package of the imported
|
||||
symbol. The importing package can be different from the original home
|
||||
package. As a result our symbol is "moved" into another package. This
|
||||
explains the new form of the value of =my-list=.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(import (first my-list) "CL-USER")
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-package 'cl-user::symb1)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
my-list
|
||||
#+END_SRC
|
||||
|
||||
Our symbol could also have been imported in other packages before being
|
||||
uninterned from its home package. The status of our symbol with respect to
|
||||
other packages would not have been modified.
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(import 'symb3 "CL-USER")
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eq 'cl-user::symb3 'symb3)
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(unintern 'symb3 "P1")
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(eq 'cl-user::symb3 (third my-list))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(import 'cl-user::symb3 "CL-USER")
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC lisp :session pcl :exports both :package "P1" :results value verbatim
|
||||
(symbol-package 'cl-user::symb3)
|
||||
#+END_SRC
|
||||
|
||||
Here we import =symb3= in the package ="CL-USER"= before uninterning it from
|
||||
the package ="P1"=. The symbol is then accessible via the package ="CL-USER"=
|
||||
or via the list =my-list=. The evaluation of the expression =(eq ...)= proves
|
||||
that both paths lead to the same address. When the first =(import ...)=
|
||||
expression is evaluated, the symbol does have a home package and that it is
|
||||
not changed. When it is uninterned from the package ="P1"=, it becomes
|
||||
homeless. Lisp does not take account of the fact it is internal in other
|
||||
packages. In theory, this would allow it to select one of these packages as
|
||||
an "emergency home" package, but the choice would be quite arbitrary?
|
||||
Furthermore this would force Lisp to examine the list of all other packages
|
||||
and all their accessible symbols, which would be very expensive! Here,
|
||||
despite the symbol being internal and therefore interned in the package
|
||||
="CL-USER"=, it becomes homeless after evaluating the =(unintern ...)=
|
||||
expression. Finally we import it in the package ="CL-USER"=, where, in fact,
|
||||
it is already present. According to the ANSI standard (see the following
|
||||
note), in such a situation, its status remains unchanged. In particular its
|
||||
home package remains =nil=. A little esoteric, huh? Don’t worry: the home
|
||||
package does not have any real role for the Lisp user.
|
||||
|
||||
-----
|
||||
|
||||
*Note:* The [[http://clhs.lisp.se/Body/f_import.htm#import][passage]] in the [[http://clhs.lisp.se/Front/index.htm][Common Lisp HyperSpec]] reads like this: /"If the
|
||||
symbol is already present in the importing package, import has no effect."/
|
||||
It appears that the behavior of SBCL and some other compilers is different:
|
||||
The output is =#<PACKAGE "COMMON-LISP-USER">= not =NIL=.
|
||||
|
||||
-----
|
||||
|
||||
We will prove later that if a symbol $sm$ has a home package $p$, then $sm$
|
||||
is certainly present in $p$, that is, internal or external in this package.
|
||||
The "converse" is false: a symbol may be present in several packages
|
||||
$p_1,\ldots , p_k$ while not having a home package. This might sound a little
|
||||
weird, but you shouldn't lose much sleep over it: except for the internal
|
||||
efficiency of the Lisp interpeter or compiler, the notion of a home-package
|
||||
is almost useless for the programmer.
|
||||
|
||||
* External symbols and used packages.
|
||||
|
||||
The analysis of this notion of an internal symbol was a little lengthy, but,
|
||||
in this way, the general structure of the packaging system is now very clear,
|
||||
and it will be a lot easier to understand the other concepts. Two fundamental
|
||||
lists maintained in the packages play a "dual" role in the packaging system,
|
||||
the list of /external/ symbols and the list of /used/ packages. The following
|
||||
function gives the list of the external symbols of a package.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user