└── README.org /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Common Lisp Tweaks 2 | 3 | This document collects various tweaks for working with Common Lisp. 4 | 5 | Do not hesitate to create an [[https://github.com/marcoheisig/common-lisp-tweaks/issues][issue]] or a [[https://github.com/marcoheisig/common-lisp-tweaks/pulls][pull request]] if you have a 6 | suggestion for a new tweak, or if you have an idea how an existing tweak 7 | can be tweaked further, 8 | 9 | * Table of Contents 10 | - [[#table-of-contents][Table of Contents]] 11 | - [[#general-tweaks][General Tweaks]] 12 | - [[#improve-how-emacs-indents-code][Improve how Emacs indents code]] 13 | - [[#tell-slime-to-use-clouseau-for-inspecting-objects][Tell SLIME to use Clouseau for inspecting objects]] 14 | - [[#share-one-startup-file-across-multiple-implementations][Share one startup file across multiple implementations]] 15 | - [[#customize-your-startup-file][Customize your startup file]] 16 | - [[#improve-your-quicklisp-experience][Improve your Quicklisp experience]] 17 | - [[#sbcl-specific-tweaks][SBCL-specific tweaks]] 18 | - [[#use-a-core-file-for-faster-startup][Use a core file for faster startup]] 19 | - [[#compile-sbcl-yourself][Compile SBCL yourself]] 20 | - [[#acknowledgements][Acknowledgements]] 21 | 22 | * General Tweaks 23 | :PROPERTIES: 24 | :CUSTOM_ID: tweaks 25 | :END: 26 | 27 | ** Improve how Emacs indents code 28 | :PROPERTIES: 29 | :CUSTOM_ID: improve-how-emacs-indents-code 30 | :END: 31 | 32 | Emacs is usually very smart about indenting Lisp code, but there is still 33 | room for improvement, especially when using very elaborate custom macros. 34 | Put the following code in your Emacs configuration to improve indentation: 35 | 36 | #+BEGIN_SRC elisp 37 | ;;; By default, the keyword arguments of MAKE-INSTANCE, 38 | ;;; REINITIALIZE-INSTANCE and CHANGE-CLASS are aligned with the first 39 | ;;; argument of that function. This looks ugly and wastes screen space. 40 | ;;; With the following changes, the keyword arguments align as if the 41 | ;;; non-keyword arguments wouldn't exist. 42 | (put 'make-instance 'common-lisp-indent-function 1) 43 | (put 'reinitialize-instance 'common-lisp-indent-function 1) 44 | (put 'change-class 'common-lisp-indent-function 2) 45 | 46 | ;;; Have you ever wondered how you can have your own LET, LET* or FLET 47 | ;;; indented properly? Here's how: 48 | (put 'dx-flet 'common-lisp-indent-function 49 | '((&whole 4 &rest (&whole 1 4 &lambda &body)) &body)) 50 | (put 'dx-let 'common-lisp-indent-function 51 | '((&whole 4 &rest (&whole 1 1 2)) &body)) 52 | (put 'dx-let* 'common-lisp-indent-function 53 | '((&whole 4 &rest (&whole 1 1 2)) &body)) 54 | #+END_SRC 55 | 56 | ** Tell SLIME to use Clouseau for inspecting objects 57 | :PROPERTIES: 58 | :CUSTOM_ID: tell-slime-to-use-clouseau-for-inspecting-objects 59 | :END: 60 | 61 | Clouseau is an graphical inspector for Common Lisp. SLIME's built-in 62 | inspector is great, but Clouseau is better (If you doubt it, try inspecting 63 | a complex number). 64 | 65 | With this tweak, invoking the inspector will open a new window with the 66 | graphical inspector. If there is already a window with the graphical 67 | inspector, the existing window is reused. 68 | 69 | #+BEGIN_SRC elisp 70 | (defun clouseau-inspect (string) 71 | (interactive 72 | (list (slime-read-from-minibuffer 73 | "Inspect value (evaluated): " 74 | (slime-sexp-at-point)))) 75 | (let ((inspector 'cl-user::*clouseau-inspector*)) 76 | (slime-eval-async 77 | `(cl:progn 78 | (cl:defvar ,inspector nil) 79 | ;; (Re)start the inspector if necessary. 80 | (cl:unless (cl:and (clim:application-frame-p ,inspector) 81 | (clim-internals::frame-process ,inspector)) 82 | (cl:setf ,inspector (cl:nth-value 1 (clouseau:inspect nil :new-process t)))) 83 | ;; Tell the inspector to visualize the correct datum. 84 | (cl:setf (clouseau:root-object ,inspector :run-hook-p t) 85 | (cl:eval (cl:read-from-string ,string))) 86 | ;; Return nothing. 87 | (cl:values))))) 88 | 89 | (define-key slime-prefix-map (kbd "c") 'clouseau-inspect) 90 | #+END_SRC 91 | 92 | For this code to work, you also need to have Clouseau loaded it your Common 93 | Lisp image. This can be achieved either by putting =(ql:quickload 94 | "clouseau")= into the startup file of your Common Lisp implementation, or 95 | by [[#use-a-core-file-for-faster-startup][integrating Clouseau into a core file]]. 96 | 97 | ** Share one startup file across multiple implementations 98 | :PROPERTIES: 99 | :CUSTOM_ID: share-one-startup-file-across-multiple-implementations 100 | :END: 101 | 102 | If you are using multiple Common Lisp implementations on a single machine, 103 | chances are you are annoyed at having to maintain one startup file for each 104 | one of them. There is a simple trick how you can keep your startup files 105 | in sync: Just create a single file, say =~/.init.lisp=, put all your 106 | configuration there, and then create a symbolic link for each 107 | implementation and have it point to this file. Here is how you can create 108 | such links: 109 | 110 | #+BEGIN_SRC sh 111 | ln -sT ~/.init.lisp ~/.clisprc.lisp # for CLISP 112 | ln -sT ~/.init.lisp ~/.cmucl-init.lisp # for CMUCL 113 | ln -sT ~/.init.lisp ~/.eclrc # for ECL 114 | ln -sT ~/.init.lisp ~/.ccl-init.lisp # for CCL 115 | ln -sT ~/.init.lisp ~/.sbclrc # for SBCL 116 | ln -sT ~/.init.lisp ~/.abclrc # for ABCL 117 | ln -sT ~/.init.lisp ~/.mkclrc # for MKCL 118 | #+END_SRC 119 | 120 | *Note:* You can still put implementation-specific code in your shared 121 | startup file if you use the =#+= reader macro, as in =#+sbcl(setf 122 | sb-ext:*inline-expansion-limit* 100)=. 123 | 124 | ** Customize your startup file 125 | :PROPERTIES: 126 | :CUSTOM_ID: customize-your-startup-file 127 | :END: 128 | 129 | The behavior of a Common Lisp implementation is determined by a several 130 | special variables. There default values are not always ideal for working 131 | interactively on a REPL. Here are some suggestions for improving that: 132 | 133 | #+BEGIN_SRC lisp 134 | ;; Stop Lisp from SCREAMING AT YOU. 135 | (setf *print-case* :downcase) 136 | ;; Replace very deeply nested structures by '#' when printing. 137 | (setf *print-level* 50) 138 | ;; Replace elements of very long sequences by '...' when printing. 139 | (setf *print-length* 200) 140 | #+END_SRC 141 | 142 | Another popular tweak is to globally enact a certain compiler policy. In 143 | Common Lisp, the compiler policy consists of quantities that should be 144 | optimized for, and their respective priority on a scale from zero to three. 145 | The standard optimize qualities are 146 | 147 | | Name | Meaning | 148 | |-------------------+-----------------------------------| 149 | | compilation-speed | speed of the compilation process | 150 | | debug | ease of debugging | 151 | | safety | run-time error checking | 152 | | space | both code size and run-time space | 153 | | speed | speed of the object code | 154 | 155 | Usually, any Lisp code change these policies locally via =(declare 156 | (optimize ...))= declarations, or on a per-file basis via =(declaim 157 | (optimize ...))=. However "it is unspecified whether or not the 158 | compile-time side-effects of a declaim persist after the file has been 159 | compiled", so you should be careful with the latter. 160 | 161 | Anyways, you might not want any code you compile, load, or execute to screw 162 | with your compiler policy. Luckily, SBCL has you covered and offers a way 163 | to globally enforce certain minimum and maximum values for each quantity. 164 | 165 | A popular option for development systems is to have both safety and 166 | debugging cranked up to the highest possible value. 167 | 168 | #+BEGIN_SRC lisp 169 | #+sbcl 170 | (progn 171 | (sb-ext:restrict-compiler-policy 'safety 3) 172 | (sb-ext:restrict-compiler-policy 'debug 3)) 173 | #+END_SRC 174 | 175 | *Warning:* Restricting the compiler policy in this way will affect 176 | performance quite a bit. Don't forget this before you run any benchmarks 177 | (This has happened to me several times by now). 178 | 179 | ** Improve your Quicklisp experience 180 | :PROPERTIES: 181 | :CUSTOM_ID: improve-your-quicklisp-experience 182 | :END: 183 | 184 | Git hooks, automatic updates, ... 185 | 186 | * SBCL-specific tweaks 187 | :PROPERTIES: 188 | :CUSTOM_ID: sbcl-specific-tweaks 189 | :END: 190 | 191 | ** Use a core file for faster startup 192 | :PROPERTIES: 193 | :CUSTOM_ID: use-a-core-file-for-faster-startup 194 | :END: 195 | 196 | SBCL supports loading a custom core file instead of the default, empty one. 197 | Users can create custom core files where many of their most frequently used 198 | libraries are already present to speed up startup time considerably. 199 | 200 | The only downside of using such a custom core file is that the user is now 201 | responsible for keeping the libraries therein up to date, ideally by 202 | regenerating the core file after each new Quicklisp dist update. 203 | 204 | A core file can be created like this: 205 | 206 | #+BEGIN_SRC lisp 207 | ;; Ensure that Quicklisp is up to date. 208 | (ql:update-client) 209 | (ql:update-all-dists) 210 | 211 | ;; Load libraries that you care about 212 | (dolist (system '("alexandria" 213 | "babel" 214 | "bordeaux-threads" 215 | "cffi" 216 | "closer-mop" 217 | "cl-ppcre" 218 | "cl-fad" 219 | "flexi-streams" 220 | "nibbles" 221 | "named-readtables" 222 | "split-sequence" 223 | "trivial-backtrace" 224 | "trivial-features" 225 | "trivial-garbage" 226 | "trivial-macroexpand-all" 227 | "trivial-package-local-nicknames" 228 | ;; ... 229 | )) 230 | (ql:quickload system)) 231 | 232 | (uiop:dump-image "my-core") 233 | #+END_SRC 234 | 235 | You can also tell Emacs to use your custom core by default by adding 236 | something like this to your configuration file: 237 | 238 | #+BEGIN_SRC elisp 239 | (setf slime-lisp-implementations 240 | '((sbcl-vanilla ("sbcl" "--dynamic-space-size" "8GB")) 241 | (sbcl-custom ("sbcl" "--dynamic-space-size" "8GB" "--core" "~/path/to/my-core")))) 242 | (setf silme-default-lisp 'sbcl-custom) 243 | #+END_SRC 244 | 245 | ** Compile SBCL yourself 246 | :PROPERTIES: 247 | :CUSTOM_ID: compile-sbcl-yourself 248 | :END: 249 | You know the problem: A new, exciting version of SBCL has been released. 250 | Everyone of your friends is talking about it. You desperately want those 251 | features. But your package manager is stuck in the stone age. Fret not, 252 | because compiling SBCL yourself is actually very simple: 253 | 254 | **** Install dependencies 255 | In order to compile SBCL yourself, you need an existing Lisp implementation 256 | (ideally, an older version of SBCL), Git, a shell, GNU Make and GCC. You 257 | can get this stuff with something like 258 | #+BEGIN_SRC sh 259 | apt install build-essential git sbcl 260 | #+END_SRC 261 | or with whatever syntax your package manager uses. 262 | 263 | **** Obtain the source code 264 | You can obtain the source code of SBCL from Github: 265 | #+BEGIN_SRC sh 266 | git clone https://github.com/sbcl/sbcl.git sbcl && cd sbcl 267 | #+END_SRC 268 | Usually you don't want to build the most recent commit on the master 269 | branch, but the most recent release. All SBCL releases are tagged, so you 270 | can (and should) checkout the most recent tag: 271 | #+BEGIN_SRC sh 272 | git checkout sbcl-2.X.Y 273 | #+END_SRC 274 | 275 | **** Build SBCL 276 | SBCL is build by a script called =make.sh=. You can supply some additional 277 | options to this script to customize your build. Some popular options are 278 | - =--dynamic-space-size=8Gb= for a large default heap size. 279 | - =--prefix=$HOME/usr= to put SBCL in the user's home directory later. 280 | - =--fancy= to enable all supported enhancements. 281 | - =--xc-host=LISP= (optional) specify the Lisp implementation to use for compiling. 282 | 283 | After building, you should run the test suite, build the documentation, and 284 | install everything. The following one-liner performs all these steps at 285 | once: 286 | #+BEGIN_SRC sh 287 | sh make.sh --dynamic-space-size=8Gb --prefix=$HOME/usr --fancy \ 288 | && cd tests && sh run-tests.sh && cd .. \ 289 | && cd doc/manual && make && cd ../.. \ 290 | && sh install.sh 291 | #+END_SRC 292 | 293 | *Note:* The above command ensures that SBCL is installed in =~/usr/bin=. 294 | If you want your shell to find the SBCL executable, you also need something 295 | like =export PATH=$HOME/usr/bin:$PATH= in your =.bashrc=. You can type 296 | =which sbcl= to check whether your shell is able to locate the correct 297 | version. 298 | 299 | * Acknowledgements 300 | 301 | The following people have contributed to this document in some way or 302 | another: 303 | 304 | - Michael Fiano 305 | - death 306 | - Michał phoe Herda 307 | - Marco Heisig 308 | - Jan Moringen 309 | --------------------------------------------------------------------------------