Another code-tuning tool built into Lisp is the function DISASSEMBLE
. The exact behavior of this function is implementation dependent because it depends on how the implementation compiles code—whether to machine code, bytecodes, or some other form. But the basic idea is that it shows you the code generated by the compiler when it compiled a specific function.
Thus, you can use DISASSEMBLE
to see whether your declarations are having any effect on the code generated. And if your Lisp implementation uses a native compiler and you know your platform's assembly language, you can get a pretty good sense of what's actually going on when you call one of your functions. For instance, you could use DISASSEMBLE
to get a sense of the difference between the first version of add
, with no declarations, and the final version. First, define and compile the original version.
(defun add (x y) (+ x y))
Then, at the REPL, call DISASSEMBLE
with the name of the function. In Allegro, it shows the following assembly-language-like dump of the code generated by the compiler:
CL-USER> (disassemble 'add)
;; disassembly of #<Function ADD>
;; formals: X Y
;; code start: #x737496f4:
0: 55 pushl ebp
1: 8b ec movl ebp,esp
3: 56 pushl esi
4: 83 ec 24 subl esp,$36
7: 83 f9 02 cmpl ecx,$2
10: 74 02 jz 14
12: cd 61 int $97 ; SYS::TRAP-ARGERR
14: 80 7f cb 00 cmpb [edi-53],$0 ; SYS::C_INTERRUPT-PENDING
18: 74 02 jz 22
20: cd 64 int $100 ; SYS::TRAP-SIGNAL-HIT
22: 8b d8 movl ebx,eax
24: 0b da orl ebx,edx
26: f6 c3 03 testb bl,$3
29: 75 0e jnz 45
31: 8b d8 movl ebx,eax
33: 03 da addl ebx,edx
35: 70 08 jo 45
37: 8b c3 movl eax,ebx
39: f8 clc
40: c9 leave
41: 8b 75 fc movl esi,[ebp-4]
44: c3 ret
45: 8b 5f 8f movl ebx,[edi-113] ; EXCL::+_2OP
48: ff 57 27 call *[edi+39] ; SYS::TRAMP-TWO
51: eb f3 jmp 40
53: 90 nop
; No value
Clearly, there's a bunch of stuff going on here. If you're familiar with x86 assembly language, you can probably tell what. Now compile this version of add
with all the declarations.
(defun add (x y)
(declare (optimize (speed 3) (safety 0)))
(declare (fixnum x y))
(the fixnum (+ x y)))
Now disassemble add
again, and see if the declarations had any effect.
CL-USER> (disassemble 'add)
;; disassembly of #<Function ADD>
;; formals: X Y
;; code start: #x7374dc34:
0: 03 c2 addl eax,edx
2: f8 clc
3: 8b 75 fc movl esi,[ebp-4]
6: c3 ret
7: 90 nop
; No value
Looks like they did.
Delivering Applications
Another topic of practical importance, which I didn't talk about elsewhere in the book, is how to deliver software written in Lisp. The main reason I neglected this topic is because there are many different ways to do it, and which one is best for you depends on what kind of software you need to deliver to what kind of user with what Common Lisp implementation. In this section I'll give an overview of some of the different options.
If you've written code you want to share with fellow Lisp programmers, the most straightforward way to distribute it is as source code.[329] You can distribute a simple library as a single source file, which programmers can LOAD
into their Lisp image, possibly after compiling it with COMPILE-FILE
.
More complex libraries or applications, broken up across multiple source files, pose an additional challenge—in order to load and compile the code, the files need to be loaded and compiled in the correct order. For instance, a file containing macro definitions must be loaded before you can compile files that use those macros. And a file containing DEFPACKAGE
forms must be loaded before any files that use those packages can even be READ
. Lispers call this the system definition problem and typically handle it with tools called system definition facilities or system definition utilities, which are somewhat analogous to build tools such as make
or ant
. As with make
and ant
, system definition tools allow you to specify the dependencies between different files and then take care of loading and compiling the files in the correct order while trying to do only work that's necessary—recompiling only files that have changed, for example.
These days the most widely used system definition tool is ASDF, which stands for Another System Definition Facility.[330] The basic idea behind ASDF is that you define systems in ASD files, and ASDF provides a number of operations on systems such as loading them or compiling them. A system can also be defined to depend on other systems, which will be loaded as necessary. For instance, the following shows the contents of html.asd
, the ASD file for the FOO library from Chapters 31 and 32:
(defpackage :com.gigamonkeys.html-system (:use :asdf :cl))
(in-package :com.gigamonkeys.html-system)
(defsystem html
:name "html"
:author "Peter Seibel <peter@gigamonkeys.com>"
:version "0.1"
:maintainer "Peter Seibel <peter@gigamonkeys.com>"
:license "BSD"
:description "HTML and CSS generation from sexps."
:long-description ""
:components
((:file "packages")
(:file "html" :depends-on ("packages"))
(:file "css" :depends-on ("packages" "html")))
:depends-on (:macro-utilities))
If you add a symbolic link to this file from a directory listed in asdf:*central-registry*
,[331] then you can type this:
(asdf:operate 'asdf:load-op :html)
to compile and load the files packages.lisp
, html.lisp
, and html-macros.lisp
in the correct order after first making sure the :macro-utilities
system has been compiled and loaded. For other examples of ASD files, you can look at this book's source code—the code from each practical chapter is defined as a system with appropriate intersystem dependencies expressed in the ASD files.
329
The FASL files produced by COMPILE-FILE
are implementation dependent and may or may not be compatible between different versions of the same Common Lisp implementation. Thus, they're not a very good way to distribute Lisp code. The one time they can be handy is as a way of providing patches to be applied to an application running in a known version of a particular implementation. Applying the patch simply entails LOAD
ing the FASL, and because a FASL can contain arbitrary code, it can be used to upgrade existing data as well as to provide new code definitions.
330
ASDF was originally written by Daniel Barlow, one of the SBCL developers, and has been included as part of SBCL for a long time and also distributed as a stand-alone library. It has recently been adopted and included in other implementations such as OpenMCL and Allegro.
331
On Windows, where there are no symbolic links, it works a little bit differently but roughly the same.