STANDARD-VALUE*PRINT-BASE* might be bound temporarily to 3 or *PACKAGE* might be bound to the KEYWORD package.
Entry to the debugger or some other form of recursive read-eval-print loop may happen either asynchronously (in some implementations) or synchronously due to a call to ERROR or BREAK. When that happens, if these option variables are bound to invalid values, it is most useful to bind them back to something useful.
Unfortunately, Common Lisp does not provide a way of distinguishing ``I'm binding/setting this variable for a temporary purpose which should not affect recursive read-eval-print loops.'' from ``I'm binding/setting this variable for the express purpose of affecting recursive read-eval-print loops.''
So, for example,
(DEFUN PRINT-IN-KEYWORD-PACKAGE (X) (LET ((*PRINT-BASE* (FIND-PACKAGE "KEYWORD"))) (PRINT XX))) (PRINT-IN-KEYWORD-PACKAGE 'ZAP) >>Error: The variable XX is unbound. Debug> (HELP) >>Error: The function :HELP is undefined.
It might be useful for *PACKAGE* to have been bound back to something other than keyword here.
(DEFUN BASE-3-REPL () (LET ((*PRINT-BASE* 3) (*READ-BASE* 3) (*PRINT-RADIX* NIL)) (MY-READ-EVAL-PRINT-LOOP :PROMPT "Base 3> "))) Base 3> (- 10 1) 2 Base 3> (+ 'FOO 1) Error: FOO is not a valid argument to the + function. Debug> :Use-Value 10 2
Here it was useful that *PRINT-BASE* was not bound back to something.
DEFVAR-STANDARD name value [Macro]
Like DEFVAR, but the initial value is recorded as the `standard value.'
LET-STANDARD let-bindings &body forms [Macro]
Like LET, but binds both the values and the standard values of the indicated variables (which must have been previously defined by DEFVAR-STANDARD).
PROGV-STANDARD names values &body forms [Macro]
Like PROGV, but binds both the values and the standard values of the indicated variables (which must have been previously defined by DEFVAR-STANDARD).
STANDARD-VALUE name [Function]
Returns the standard value of the indicated variable. Note that this may not be the same as (SYMBOL-VALUE name).
SETF may be used with this function. Note that setting the standard value of NAME does not set (SYMBOL-VALUE name).
When the debugger is entered, variables which have been declared standard but which do not currently have their standard values (as judged by EQL) will be rebound to their standard values (and a mechanism will be provided for viewing the values to which they were bound at debugger entry time).
NON-STANDARD-VALUES [Function]
Returns a list of variables which are not currently bound to their standard values.
The following variables in the LISP package are defined as if by DEFVAR-STANDARD:
*PRINT-ARRAY* *PRINT-GENSYM* *READ-BASE* *PRINT-BASE* *PRINT-LENGTH* *READ-DEFAULT-FLOAT-FORMAT* *PRINT-CASE* *PRINT-LEVEL* *READ-SUPPRESS* *PRINT-CIRCLE* *PRINT-PRETTY* *READTABLE* *PRINT-ESCAPE* *PRINT-RADIX*
Define that LOAD and COMPILE-FILE bind *PACKAGE* using LET-STANDARD.
Define that IN-PACKAGE sets both the SYMBOL-VALUE and the STANDARD-VALUE of the variable in question.
(DEFUN PRINT-IN-KEYWORD-PACKAGE (X) (LET ((*PRINT-BASE* (FIND-PACKAGE "KEYWORD"))) (PRINT XX))) (PRINT-IN-KEYWORD-PACKAGE 'ZAP) >>Error: The variable XX is unbound. Rebinding *PACKAGE* from #<PACKAGE "KEYWORD"> to #<PACKAGE "USER">. Debug> (HELP) ...This is presumably typeout from some function called HELP...
(DEFUN BASE-3-REPL () (LET-STANDARD ((*PRINT-BASE* 3) (*READ-BASE* 3) (*PRINT-RADIX* NIL)) (MY-READ-EVAL-PRINT-LOOP :PROMPT "Base 3> "))) Base 3> (- 10 1) 2 Base 3> (+ 'FOO 1) Error: FOO is not a valid argument to the + function. Debug> :Use-Value 10 2
Also, users of Genera (where standard values already exist) have complained that IN-PACKAGE does not set the standard value of *PACKAGE* in Genera. The same is true for the binding of *PACKAGE* by LOAD. Other users would complain if standard values were affected. The confusion is not really due to the fact one meaning is preferrable over the other, but rather due to the fact that Common Lisp cannot document such a meaning without first admitting the concept of standard values. If standard values existed in Common Lisp and the documentation was clear on how IN-PACKAGE and LOAD related to that concept, users would no longer be forced to guess what these functions did with respect to such information, and so those users who are now surprised would no longer have reason to be surprised.
(DEFVAR *STANDARD-VALUES* '())
(DEFUN STANDARD-VALUE (NAME) (LET ((ENTRY (ASSOC NAME *STANDARD-VALUES*))) (IF (NOT ENTRY) (ERROR ...) (CADR ENTRY))))
(DEFUN SET-STANDARD-VALUE (NAME VALUE) (LET ((ENTRY (ASSOC NAME *STANDARD-VALUES*))) (IF (NOT ENTRY) (ERROR ...) (SETF (CADR ENTRY) VALUE))))
(DEFSETF STANDARD-VALUE SET-STANDARD-VALUE)
(DEFMACRO DEFVAR-STANDARD (NAME VALUE) `(PROGN (DEFVAR ,NAME ,VALUE) (PUSHNEW (CONS ',NAME ,NAME) *STANDARD-VALUES* :KEY #'CAR) ',NAME))
(DEFMACRO LET-STANDARD (BINDINGS &BODY FORMS) `(LET ,@BINDINGS (LET ((*STANDARD-VALUES* (LIST* ,@(MAPCAR #'(LAMBDA (BINDING) `(CONS ',(CAR BINDING) ,(CAR BINDING))) BINDINGS) *STANDARD-VALUES*))) ,@FORMS)))
(DEFMACRO PROGV-STANDARD (VARS VALUES &BODY FORMS)
(LET ((TEMP (GENSYM)))
`(LET ((,TEMP ,VARS))
(PROGV ,TEMP ,VALUES
(LET ((*STANDARD-VALUES* *STANDARD-VALUES*))
;; This puts them on backward from how LET-STANDARD does, but
;; if there are no duplicates, that won't matter... and
;; duplicates are disallowed as per LET.
(DOLIST (,TEMP ,TEMP)
(PUSH (CONS ,TEMP (SYMBOL-VALUE ,TEMP)) *STANDARD-VALUES*))
,@FORMS)))))
(DEFUN NON-STANDARD-VALUES () (LET ((RESULT '())) (DOLIST (ENTRY *STANDARD-VALUES*) (UNLESS (EQL (SYMBOL-VALUE (CAR ENTRY)) (CDR ENTRY)) (PUSH (CAR ENTRY) RESULT))) RESULT))
[I wrote these quickly and didn't test them. If anyone else reviewing this has time to test them and let me know about bugs, that would be nice. -kmp]
However, the hair is easy to overlook the nagging little problems that come from not using this facility. The problem is that, without a facility like this, those who do care about these issues have no way to express their intent.
The problem with the meaning of IN-PACKAGE was first pointed out to Pitman by Rees, who encountered the problem while creating his portable Scheme implementation.