;;; Packages in Common Lisp. I create a new, empty package "P1", which has nothing ;;; and doesn't even specify that it needs to import standard "LISP" functions. ;;; Then I re-import all of LISP manually, but not before I fight name collisions ;;; from symbols I accidentally created in "P1" when trying to call standard LISP ;;; functions. These calls did not succeed, but created ("interned") symbols ;;; in "P1"'s symbol table that would collide with the same symbols in the "LISP" ;;; package. For this reason, packages are declared with DEFPACKAGE, which ;;; takes care of importing "LISP" automatically. See actual Quicklisp packages ;;; like LTK for examples. >*package* #<"COMMON-LISP-USER" package> ;; Create a package. This creates a separate namespace "P1", a new global table for symbols. >(MAKE-PACKAGE "p1") #<"p1" package> ;; Now _enter_ enter this new space, i.e., only use "P1" symbol table for symbol lookups. ;; This is foolhardy---we won't have any of LISP's standard symbols/functions there! >(IN-PACKAGE "p1") #<"p1" package> ;; No + symbol in "P1". But one will be created by REPL's Read, and will get in the way later. p1>(+ 1 3) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on +: Undefined function: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; No CONS either. p1>(cons 1 2) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on CONS: Undefined function: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; To access these functions inside "P1", we need to give their full package names. They are in package "LISP". p1>(LISP:+ 1 3) 4 p1>(lisp:cons 1 2) (1 . 2) ;; Remember: giving Read a symbol will create it, even though it's not bound to anything and thus ;; evaluating it leads to error. p1>x Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on X: Unbound variable: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>(lisp:FIND-SYMBOL "X" "P1") X :INTERNAL ;; One exists in LISP, too, from our previous messing about. These are different symbols. p1>(lisp:FIND-SYMBOL "X" "LISP") LISP::X :INTERNAL p1>varzilla Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on VARZILLA: Unbound variable: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; But symbols I accidentally create in "P1" will not pollute other packages. VARZILLA now exists ;; as a symbol in "P1", but not in "LISP". p1>(lisp:FIND-SYMBOL "VARZILLA" "LISP") LISP:NIL LISP:NIL p1>*package* ;; Oops, *package* does not exist in "P1". It is just a specially named variable, created by the DEFPACKAGE macro. Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on *PACKAGE*: Unbound variable: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; Still no bare CONS, as before. But what if we wanted to use all of "LISP" symbols in "P1"? p1>(cons 3 4) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on CONS: Undefined function: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; We need to tell GCL to look symbols up in the "LISP" symbol table if they are not found in "P1"'s table. ;; But it's not so easy---we already created some local symbols such as CONS and + that would be in ;; _both_ symbol tables, and would thus collide. So USE-PACKAGE refuses to run! ;; If you want to see how to recover from this without fumbling, jump to SANE-FROM-HERE below. p1>(LISP:USE-PACKAGE "LISP") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; Nope, not the full form either: p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; Did I misspell use-package? No. p1>(LISP: "LISP" "P1") Display all 954 possibilities? (y or n) p1>(LISP:U "LISP" "P1") LISP:UNBOUND-SLOT LISP:UNREAD-CHAR LISP:UPGRADED-ARRAY-ELEMENT-TYPE LISP:UNBOUND-VARIABLE LISP:UNSIGNED-BYTE LISP:UPPER-CASE-P LISP:UNDEFINED-FUNCTION LISP:UNSIGNED-CHAR LISP:USE-PACKAGE LISP:UNEXPORT LISP:UNSIGNED-SHORT LISP:USE-VALUE LISP:UNINTERN LISP:UNTRACE LISP:USER-HOMEDIR-PATHNAME LISP:UNION LISP:UNUSE-PACKAGE LISP:UNLESS LISP:UNWIND-PROTECT p1>(LISP:U "LISP" "P1") LISP:UNBOUND-SLOT LISP:UNREAD-CHAR LISP:UPGRADED-ARRAY-ELEMENT-TYPE LISP:UNBOUND-VARIABLE LISP:UNSIGNED-BYTE LISP:UPPER-CASE-P LISP:UNDEFINED-FUNCTION LISP:UNSIGNED-CHAR LISP:USE-PACKAGE LISP:UNEXPORT LISP:UNSIGNED-SHORT LISP:USE-VALUE LISP:UNINTERN LISP:UNTRACE LISP:USER-HOMEDIR-PATHNAME LISP:UNION LISP:UNUSE-PACKAGE LISP:UNLESS LISP:UNWIND-PROTECT p1>(LISP:USE "LISP" "P1") LISP:USE-PACKAGE LISP:USE-VALUE LISP:USER-HOMEDIR-PATHNAME p1>(LISP:USE "LISP" "P1") LISP:USE-PACKAGE LISP:USE-VALUE LISP:USER-HOMEDIR-PATHNAME p1>(LISP:USE- "LISP" "P1") LISP:USE-PACKAGE LISP:USE-VALUE p1>(LISP:USE- "LISP" "P1") LISP:USE-PACKAGE LISP:USE-VALUE p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; We need to UNINTERN the conflicting symbols from "P1" first. (Tab-completing for UNINTERN in "LISP") p1>(LISP:UN LISP:UNBOUND-SLOT LISP:UNINTERN LISP:UNSIGNED-BYTE LISP:UNUSE-PACKAGE LISP:UNBOUND-VARIABLE LISP:UNION LISP:UNSIGNED-CHAR LISP:UNWIND-PROTECT LISP:UNDEFINED-FUNCTION LISP:UNLESS LISP:UNSIGNED-SHORT LISP:UNEXPORT LISP:UNREAD-CHAR LISP:UNTRACE p1>(LISP:UN LISP:UNBOUND-SLOT LISP:UNINTERN LISP:UNSIGNED-BYTE LISP:UNUSE-PACKAGE LISP:UNBOUND-VARIABLE LISP:UNION LISP:UNSIGNED-CHAR LISP:UNWIND-PROTECT LISP:UNDEFINED-FUNCTION LISP:UNLESS LISP:UNSIGNED-SHORT LISP:UNEXPORT LISP:UNREAD-CHAR LISP:UNTRACE p1>(LISP:UNI LISP:UNINTERN LISP:UNION p1>(LISP:UNI LISP:UNINTERN LISP:UNION p1>(LISP:UNINTERN "X") ;; Oops, it needs a symbol, not a string with symbol's name Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:UNINTERN. Condition in LISP:UNINTERN [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: "X" is not of type LISP:SYMBOL: Broken at LISP:UNINTERN. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>(LISP:UNINTERN 'x) LISP:T ;; OK, we nuked X . But there are other conflicting symbols. p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; Now let's list them all. There aren't many in "P1", luckily. p1>(LISP:DO) LISP:DO LISP:DO-SYMBOLS LISP:DOUBLE LISP:DO* LISP:DOCUMENTATION LISP:DOUBLE-FLOAT LISP:DO-ALL-SYMBOLS LISP:DOLIST LISP:DOUBLE-FLOAT-EPSILON LISP:DO-EXTERNAL-SYMBOLS LISP:DOTIMES LISP:DOUBLE-FLOAT-NEGATIVE-EPSILON p1>(LISP:DO) LISP:DO LISP:DO-SYMBOLS LISP:DOUBLE LISP:DO* LISP:DOCUMENTATION LISP:DOUBLE-FLOAT LISP:DO-ALL-SYMBOLS LISP:DOLIST LISP:DOUBLE-FLOAT-EPSILON LISP:DO-EXTERNAL-SYMBOLS LISP:DOTIMES LISP:DOUBLE-FLOAT-NEGATIVE-EPSILON p1>(LISP:DO-ALL-SYMBOLS (S "P1") ) "P1" ;; Um, wrong loop body. Made me think that I accidentally created a colliding NIL. That was not the case, ;; but I tried to unintern it, and actually created it instead. Argh. p1>(LISP:DO-SYMBOLS (S "P1") ) LISP:NIL p1>(LISP:UN LISP:UNBOUND-SLOT LISP:UNINTERN LISP:UNSIGNED-BYTE LISP:UNUSE-PACKAGE LISP:UNBOUND-VARIABLE LISP:UNION LISP:UNSIGNED-CHAR LISP:UNWIND-PROTECT LISP:UNDEFINED-FUNCTION LISP:UNLESS LISP:UNSIGNED-SHORT LISP:UNEXPORT LISP:UNREAD-CHAR LISP:UNTRACE p1>(LISP:UNINTERN nil) ;; Oops, now we created a NIL symbol in "P1" for no good reason :( Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on NIL: Unbound variable: Broken at LISP:UNINTERN. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>(LISP:UNINTERN 'nil) LISP:T ;; Still conflicts! I forgot about CONS and + . p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>(LISP:UNINTERN 'LISP:NIL) LISP:NIL p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; OK, now that I got the body of DO-SYMBOLS right, let's be methodical about it. p1>(LISP:DO-SYMBOLS (S "P1") (print S)) ;; Oops, there was no PRINT in "P1", but now there is! Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:TAGBODY. Condition in LISP:TAGBODY [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on PRINT: Undefined function: Broken at LISP:TAGBODY. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; SANE-FROM-HERE: ;; ;; So, these are the symbols I stupidly created in "P1". (Except for VARZILLA---I created it on purpose, and S, ;; which was needed for DO-SYMBOLS): p1>(LISP:DO-SYMBOLS (S "P1") (LISP:print S)) + S PRINT *PACKAGE* VARZILLA CONS LISP:NIL ;; Let's nuke them all. p1>(LISP:UNINTERN 'CONS) LISP:T p1>(LISP:UNINTERN 'VARZILLA) LISP:T p1>(LISP:UNINTERN '+) LISP:T p1>(LISP:UNINTERN 'PRINT) LISP:T p1>(LISP:DO-SYMBOLS (S "P1") (LISP:print S)) S *PACKAGE* LISP:NIL p1>(LISP:UNINTERN 'S) LISP:T ;; Argh, I forgot *PACKAGE*. It's just a symbol, like any other, created by DEFPACKAGE as a convenience. p1>(LISP:USE-PACKAGE "LISP" "P1") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"p1" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>(LISP:UNINTERN 'LISP:NIL) LISP:NIL p1>(LISP:DO-SYMBOLS (S "P1") (LISP:print S)) S *PACKAGE* LISP:NIL p1>(LISP:UNINTERN '*PACKAGE*) LISP:T p1>(LISP:UNINTERN 'S) LISP:T ;; OK, FINALLY! p1>(LISP:USE-PACKAGE "LISP" "P1") T ;; Now that USE-PACKAGE succeeds, any symbol not found in "P1" is automatically looked up in "LISP", ;; so we get all of LISP back: p1>(cons 1 3) (1 . 3) ;; Other packages to consult when symbols are missing in a package are kept in a special list, ;; which we can examine with PACKAGE-USE-LIST : p1>(PACKAGE- PACKAGE-ERROR PACKAGE-NAME PACKAGE-SHADOWING-SYMBOLS PACKAGE-USED-BY-LIST PACKAGE-ERROR-PACKAGE PACKAGE-NICKNAMES PACKAGE-USE-LIST p1>(PACKAGE-USE-LIST ) ;; oops, needs a package name as an argument Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by PACKAGE-USE-LIST. Condition in PACKAGE-USE-LIST [or a callee]: INTERNAL-SIMPLE-PROGRAM-ERROR: PACKAGE-USE-LIST [or a callee] requires more than zero arguments. Broken at PACKAGE-USE-LIST. Type :H for Help. 1 Return to top level. p1>>1 Top level. p1>PACKAGE-USE-LIST ;; Oops, it's not a symbol, it's a function. Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on PACKAGE-USE-LIST: Unbound variable: Broken at EVAL. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; So, for "P1" it's "LISP", as we just set it. p1>(PACKAGE-USE-LIST "P1") (#<"LISP" package>) p1>(+ 1 2) 3 ;; Now, thanks to USE-PACKAGE, in "P1" those symbols from "LISP" will be found, and will show as inherited: p1>(FIND-SYMBOL "+") + :INHERITED ;; Let's go back to where we were, the default package for command-shell interactions. ;; Note that it's not "LISP", it's "COMMON-LISP-USER", so that we don't pollute "LISP" with ;; command-like puttering. p1>(IN-PACKAGE "LISP-USER") ;; wrong name, actually Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by IN-PACKAGE. Condition in IN-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on "LISP-USER": No such package Broken at IN-PACKAGE. Type :H for Help. 1 Return to top level. p1>>1 Top level. ;; Now we are really back. p1>(IN-PACKAGE "COMMON-LISP-USER") #<"COMMON-LISP-USER" package> ;; In this namespace, we have a "+". It's the true LISP:+ , as EQ will show as we look it up. >(FIND-SYMBOL "+") + :INTERNAL >(eq (FIND-SYMBOL "+") (FIND-SYMBOL "+" "LISP")) T ;; Finally, exported symbols will show as "external" if we ask the package for them ;; via FIND-SYMBOL: >(FIND-SYMBOL "+" "LISP") + :EXTERNAL ;;; ----------------------- Update after class: ----------------------- ;; You might wonder how FIND-SYMBOL appears to return _two_ values rather than one. ;; In fact, LISP functions can do so, by using the VALUES form. To catch ;; the additional values beyond the first one, the caller must use MULTIPLE-VALUE-BIND, ;; which looks like a LAMBDA or a LET. The first list specifies the variables to ;; catch the multiple returned values, the middle list is the actual function call, ;; and the third one is a body like defun's or lambda's showing what to do with ;; the values once they are caught into variables. Like, make a list out of them: >(MULTIPLE-VALUE-BIND (a b) (FIND-SYMBOL "CONS" "LISP") (list a b)) (CONS :EXTERNAL) ;; Or a dotted cons cell: >(MULTIPLE-VALUE-BIND (a b) (FIND-SYMBOL "CONS" "LISP") (cons a b)) (CONS . :EXTERNAL) ;; It's actually a macro. The weird #:G1967 symbol is created by the function GENSYM, ;; which gives unique temporary symbols. Next call to GEMSYM will increase its ;; global counter by 1, usually. >(MACROEXPAND '(MULTIPLE-VALUE-BIND (a b) (FIND-SYMBOL "CONS" "LISP") (list a b))) (LET* ((#:G1967 (MULTIPLE-VALUE-LIST (FIND-SYMBOL "CONS" "LISP"))) (A (NTH 0 #:G1967)) (B (NTH 1 #:G1967))) (LIST A B)) T ;; >(GENSYM) #:G1968 ;; See https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node94.html for more details.