Our next potentially unfamiliar Lisp feature is locally. Before proceeding, I would recommend that you review the articles on declare and declaim.
The locally special operator gives us an intermediate control between declare and declaim. We had declaim, which causes a change in the compiler behaviour that persists until explicitly reset, but possibly resetting at the end of the compilation unit, at the discretion of the implementers. We had declare, which affects the behaviour of the compiler within the current form. Now, with locally, we have a way to change compiler behaviour over several forms, but with an explicit end to this change. Here’s an example of how this might be used. In this file, we have three functions. Two of them have been debugged, and we want to optimize them because they’re expensive functions that slow down our testing. The third function is buggy, and we want to turn on debugging settings to try to help solve the issues. So, we declaim the compiler settings for optimization in the file as a whole, but use locally to switch over to debug settings for the one function we want to compile that way:
(declaim (optimize (debug 0) (safety 0) (speed 3))) (eval-when (:compile-toplevel) (format t "~%~%~%About to compile normal-1~%") (sb-ext:describe-compiler-policy)) (defun normal-1 (x) (declare (sb-ext:muffle-conditions sb-ext:compiler-note)) (+ 2 x)) (locally (declare (optimize (debug 3) (safety 3) (speed 0))) (eval-when (:compile-toplevel) (format t "~%~%~%About to compile debugging-1~%") (sb-ext:describe-compiler-policy)) (defun debugging-1 (x) (declare (sb-ext:muffle-conditions sb-ext:compiler-note)) (+ 3 x))) (eval-when (:compile-toplevel) (format t "~%~%~%About to compile normal-2~%") (sb-ext:describe-compiler-policy)) (defun normal-2 (x) (declare (sb-ext:muffle-conditions sb-ext:compiler-note)) (+ 4 x))
The output of compilation is as follows:
CL-USER> (compile-file "locally") ; compiling file "locally.lisp" (written 26 FEB 2014 09:06:22 PM): ; compiling (DECLAIM (OPTIMIZE # ...)) About to compile normal-1 Basic qualities: COMPILATION-SPEED = 1 DEBUG = 0 SAFETY = 0 SPACE = 1 SPEED = 3 INHIBIT-WARNINGS = 1 Dependent qualities: SB-C::CHECK-CONSTANT-MODIFICATION = 1 -> 0 (no) SB-C::TYPE-CHECK = 1 -> 0 (no) SB-C::CHECK-TAG-EXISTENCE = 1 -> 0 (no) SB-C::LET-CONVERSION = 1 -> 3 (on) SB-C:ALIEN-FUNCALL-SAVES-FP-AND-PC = 1 -> 0 (no) SB-C:VERIFY-ARG-COUNT = 1 -> 0 (no) SB-C::INSERT-DEBUG-CATCH = 1 -> 0 (no) SB-C::RECOGNIZE-SELF-CALLS = 1 -> 3 (yes) SB-C::FLOAT-ACCURACY = 1 -> 3 (full) SB-C:INSERT-STEP-CONDITIONS = 1 -> 0 (no) SB-C::COMPUTE-DEBUG-FUN = 1 -> 0 (no) SB-C::PRESERVE-SINGLE-USE-DEBUG-VARIABLES = 1 -> 0 (no) SB-C::INSERT-ARRAY-BOUNDS-CHECKS = 1 -> 0 (no) SB-C::STORE-XREF-DATA = 1 -> 3 (yes) SB-C:STORE-COVERAGE-DATA = 1 -> 0 (no) ; compiling (DEFUN NORMAL-1 ...) About to compile debugging-1 Basic qualities: COMPILATION-SPEED = 1 DEBUG = 3 SAFETY = 3 SPACE = 1 SPEED = 0 INHIBIT-WARNINGS = 1 Dependent qualities: SB-C::CHECK-CONSTANT-MODIFICATION = 1 -> 3 (yes) SB-C::TYPE-CHECK = 1 -> 3 (full) SB-C::CHECK-TAG-EXISTENCE = 1 -> 3 (yes) SB-C::LET-CONVERSION = 1 -> 0 (off) SB-C:ALIEN-FUNCALL-SAVES-FP-AND-PC = 1 -> 3 (yes) SB-C:VERIFY-ARG-COUNT = 1 -> 3 (yes) SB-C::INSERT-DEBUG-CATCH = 1 -> 3 (yes) SB-C::RECOGNIZE-SELF-CALLS = 1 -> 0 (no) SB-C::FLOAT-ACCURACY = 1 -> 3 (full) SB-C:INSERT-STEP-CONDITIONS = 1 -> 3 (full) SB-C::COMPUTE-DEBUG-FUN = 1 -> 3 (yes) SB-C::PRESERVE-SINGLE-USE-DEBUG-VARIABLES = 1 -> 3 (yes) SB-C::INSERT-ARRAY-BOUNDS-CHECKS = 1 -> 3 (yes) SB-C::STORE-XREF-DATA = 1 -> 3 (yes) SB-C:STORE-COVERAGE-DATA = 1 -> 0 (no) ; compiling (DEFUN DEBUGGING-1 ...) About to compile normal-2 Basic qualities: COMPILATION-SPEED = 1 DEBUG = 0 SAFETY = 0 SPACE = 1 SPEED = 3 INHIBIT-WARNINGS = 1 Dependent qualities: SB-C::CHECK-CONSTANT-MODIFICATION = 1 -> 0 (no) SB-C::TYPE-CHECK = 1 -> 0 (no) SB-C::CHECK-TAG-EXISTENCE = 1 -> 0 (no) SB-C::LET-CONVERSION = 1 -> 3 (on) SB-C:ALIEN-FUNCALL-SAVES-FP-AND-PC = 1 -> 0 (no) SB-C:VERIFY-ARG-COUNT = 1 -> 0 (no) SB-C::INSERT-DEBUG-CATCH = 1 -> 0 (no) SB-C::RECOGNIZE-SELF-CALLS = 1 -> 3 (yes) SB-C::FLOAT-ACCURACY = 1 -> 3 (full) SB-C:INSERT-STEP-CONDITIONS = 1 -> 0 (no) SB-C::COMPUTE-DEBUG-FUN = 1 -> 0 (no) SB-C::PRESERVE-SINGLE-USE-DEBUG-VARIABLES = 1 -> 0 (no) SB-C::INSERT-ARRAY-BOUNDS-CHECKS = 1 -> 0 (no) SB-C::STORE-XREF-DATA = 1 -> 3 (yes) SB-C:STORE-COVERAGE-DATA = 1 -> 0 (no) ; compiling (DEFUN NORMAL-2 ...) ; locally.fasl written ; compilation finished in 0:00:00.004 #P"locally.fasl" NIL NIL
I’ve used some SBCL-specific features to report the compilation settings, and also to suppress compilation notes that otherwise would complicate this output. You can see that the normal-1 and normal-2 functions are compiled with optimizations, while the debugging-1 function is compiled with debug settings.