692 lines
No EOL
47 KiB
HTML
692 lines
No EOL
47 KiB
HTML
<HTML><HEAD><TITLE>Conclusion: What's Next?</TITLE><LINK REL="stylesheet" TYPE="text/css" HREF="style.css"/></HEAD><BODY><DIV CLASS="copyright">Copyright © 2003-2005, Peter Seibel</DIV><H1>32. Conclusion: What's Next?</H1><P>I hope by now you're convinced that the title of this book isn't an
|
||
oxymoron. However, it's quite likely there's some area of programming
|
||
that's of great practical importance to you that I haven't discussed
|
||
at all. For instance, I haven't said anything about how to develop
|
||
graphical user interfaces (GUIs), how to connect to relational
|
||
databases, how to parse XML, or how to write programs that act as
|
||
clients for various network protocols. Similarly, I haven't discussed
|
||
two topics that will become important when you write real applications
|
||
in Common Lisp: optimizing your Lisp code and packaging your
|
||
application for delivery.</P><P>I'm obviously not going to cover all these topics in depth in this
|
||
final chapter. Instead, I'll give you a few pointers you can use to
|
||
pursue whichever aspect of Lisp programming interests you most.</P><A NAME="finding-lisp-libraries"><H2>Finding Lisp Libraries</H2></A><P>While the standard library of functions, data types, and macros that
|
||
comes with Common Lisp is quite large, it provides only
|
||
general-purpose programming constructs. Specialized tasks such as
|
||
writing GUIs, talking to databases, and parsing XML require libraries
|
||
beyond what are provided by the ANSI standardized language.</P><P>The easiest way to obtain a library to do something you need may be
|
||
simply to check out your Lisp implementation. Most implementations
|
||
provide at least some facilities not specified in the language
|
||
standard. The commercial Common Lisp vendors tend to work especially
|
||
hard at providing additional libraries for their implementation in
|
||
order to justify their prices. Franz's Allegro Common Lisp,
|
||
Enterprise Edition, for instance, comes with libraries for parsing
|
||
XML, speaking SOAP, generating HTML, connecting to relational
|
||
databases, and building graphical interfaces in various ways, among
|
||
others. LispWorks, another prominent commercial Lisp, provides
|
||
several similar libraries, including a well-regarded portable GUI
|
||
toolkit, CAPI, which can be used to develop GUI applications that
|
||
will run on any operating system LispWorks runs on.</P><P>The free and open-source Common Lisp implementations typically don't
|
||
include quite so many bundled libraries, relying instead on portable
|
||
free and open-source libraries. But even those implementations
|
||
usually fill in some of the more important areas not addressed by the
|
||
language standard such as networking and multithreading.</P><P>The only disadvantage of using implementation-specific libraries is
|
||
that they tie you to the implementation that provides them. If you're
|
||
delivering end-user apps or are deploying a server-based application
|
||
on a server that you control, that may not matter a lot. But if you
|
||
want to write code to share with other Lispers or if you simply don't
|
||
want to be tied to a particular implementation, it's a little more
|
||
annoying.</P><P>For portable libraries--portable either because they're written
|
||
entirely in standard Common Lisp or because they contain appropriate
|
||
read-time conditionalization to work on multiple
|
||
implementations<SUP>1</SUP>--your best bet
|
||
is to go to the Web. With the usual caveats about URLs going stale as
|
||
soon as they're printed on paper, these are three of the best current
|
||
starting points:</P><UL><LI>Common-Lisp.net (<CODE>http://www.common-lisp.net/</CODE>) is a site
|
||
that hosts free and open-source Common Lisp projects, providing
|
||
version control, mailing lists, and Web hosting of project pages. In
|
||
the first year and a half after the site went live, nearly a hundred
|
||
projects were registered.</LI><LI>The Common Lisp Open Code Collection (CLOCC)
|
||
(<CODE>http://clocc.sourceforge.net/</CODE>) is a slightly older collection
|
||
of free software libraries, which are intended to be portable between
|
||
Common Lisp implementations and self-contained, not relying on any
|
||
libraries not included in CLOCC itself. </LI><LI>Cliki (<CODE>http://www.cliki.net/</CODE>) is a wiki devoted to free
|
||
software in Common Lisp. While, like any wiki, it may change at any
|
||
time, typically it has quite a few links to libraries as well to
|
||
various open-source Common Lisp implementations. The eponymous
|
||
software it runs on is also written in Common Lisp.</LI></UL><P>Linux users running the Debian or Gentoo distributions can also
|
||
easily install an ever-growing number of Lisp libraries that have
|
||
been packaged with those distributions' packing tools, <CODE>apt-get</CODE>
|
||
on Debian and <CODE>emerge</CODE> on Gentoo.</P><P>I won't recommend any specific libraries here since the library
|
||
situation is changing every day--after years of envying the library
|
||
collections of Perl, Python, and Java, Common Lispers have, in the
|
||
past couple of years, begun to take up the challenge of giving Common
|
||
Lisp the set of libraries--both open source and commercial--that it
|
||
deserves.</P><P>One area where there has been a lot of activity recently is on the
|
||
GUI front. Unlike Java and C# but like Perl, Python, and C, there's
|
||
no single way to develop GUIs in Common Lisp. Instead, it depends
|
||
both on what Common Lisp implementation you're using and what
|
||
operating system or systems you want to support.</P><P>The commercial Common Lisp implementations usually provide some way
|
||
to build GUIs for the platforms they run on. Additionally, LispWorks
|
||
provides CAPI, the previously mentioned, portable GUI API.</P><P>On the open-source side, you have a number of options. On Unix, you
|
||
can write low-level X Windows GUIs using CLX, a pure-Common Lisp
|
||
implementation of the X Windows protocol, roughly akin to xlib in C.
|
||
Or you can use various bindings to higher-level APIs and toolkits such
|
||
as GTK and Tk, much the way you might in Perl or Python.</P><P>Or, if you're looking for something completely different, you can
|
||
check out Common Lisp Interface Manager (CLIM). A descendant of the
|
||
Symbolics Lisp Machines GUI framework, CLIM is powerful but complex.
|
||
Although many commercial Common Lisp implementations actually support
|
||
it, it doesn't seem to have seen a lot of use. But in the past couple
|
||
years, an open-source implementation of CLIM, McCLIM--now hosted at
|
||
Common-Lisp.net--has been picking up steam lately, so we may be on
|
||
the verge of a CLIM renaissance.</P><A NAME="interfacing-with-other-languages"><H2>Interfacing with Other Languages</H2></A><P>While many useful libraries can be written in "pure" Common Lisp
|
||
using only the features specified in the language standard, and many
|
||
more can be written in Lisp using nonstandard facilities provided by
|
||
a given implementation, occasionally it's more straightforward to use
|
||
an existing library written in another language, such as C.</P><P>The language standard doesn't specify a mechanism for Lisp code to
|
||
call code written in another language or even require that
|
||
implementations provide such a mechanism. But these days, almost all
|
||
Common Lisp implementations support what's called a <I>Foreign
|
||
Function Interface</I>, or FFI for short.<SUP>2</SUP> The basic job of an FFI is to allow
|
||
you to give Lisp enough information to be able to link in the foreign
|
||
code. Thus, if you're going to call a function from a C library, you
|
||
need to tell Lisp about how to translate the Lisp objects passed to
|
||
the function into C types and the value returned by the function back
|
||
into a Lisp object. However, each implementation provides its own
|
||
FFI, each with slightly varying capabilities and syntax. Some FFIs
|
||
allow callbacks from C to Lisp, and others don't. The Universal
|
||
Foreign Function Interface (UFFI) project provides a portability
|
||
layer over the FFIs of more than a half dozen different Common Lisp
|
||
implementations. It works by defining its own macros that expand into
|
||
appropriate FFI code for the implementation it's running in. The UFFI
|
||
takes a lowest common denominator approach, which means it can't take
|
||
advantage of all the features of different implementations' FFIs, but
|
||
it does provide a good way to build a simple Lisp wrapper around a
|
||
basic C API.<SUP>3</SUP></P><A NAME="make-it-work-make-it-right-make-it-fast"><H2>Make It Work, Make It Right, Make It Fast</H2></A><P>As has been said many times, and variously attributed to Donald
|
||
Knuth, C.A.R. Hoare, and Edsger Dijkstra, premature optimization is
|
||
the root of all evil.<SUP>4</SUP> Common Lisp is an
|
||
excellent language to program in if you want to heed this wisdom yet
|
||
still need high performance. This may come as a surprise if you've
|
||
heard the conventional wisdom that Lisp is slow. In Lisp's earliest
|
||
days, when computers were programmed with punch cards, Lisp's
|
||
high-level features may have doomed it to be slower than the
|
||
competition, namely, assembly and FORTRAN. But that was a long time
|
||
ago. In the meantime, Lisp has been used for everything from creating
|
||
complex AI systems to writing operating systems, and a lot of work
|
||
has gone into figuring out how to compile Lisp into efficient code.
|
||
In this section I'll talk about some of the reasons why Common Lisp
|
||
is an excellent language for writing high-performance code and some
|
||
of the techniques for doing so.</P><P>The first reason that Lisp is an excellent language for writing
|
||
high-performance code is, ironically enough, the dynamic nature of
|
||
Lisp programming--the very thing that originally made it hard to bring
|
||
Lisp's performance up to the levels achieved by FORTRAN compilers. The
|
||
reason Common Lisp's dynamic features make it easier to write
|
||
high-performance code is that the first step to writing efficient code
|
||
is to find the right algorithms and data structures.</P><P>Common Lisp's dynamic features keep code flexible, which makes it
|
||
easier to try different approaches. Given a finite amount of time to
|
||
write a program, you're much more likely to end up with a
|
||
high-performance version if you don't spend a lot of time getting
|
||
into and out of dead ends. In Common Lisp, you can try an idea, see
|
||
it's going nowhere, and move on without having spent a ton of time
|
||
convincing the compiler your code is worthy of being run and then
|
||
waiting for it to finish compiling. You can write a straightforward
|
||
but inefficient version of a function--a <I>code sketch</I>--to
|
||
determine whether your basic approach is sound and then replace that
|
||
function with a more complex but more efficient implementation if you
|
||
determine that it is. And if the overall approach turns out to be
|
||
flawed, then you haven't wasted a bunch of time tuning a function
|
||
that's no longer needed, which means you have more time to find a
|
||
better approach.</P><P>The next reason Common Lisp is a good language for developing
|
||
high-performance software is that most Common Lisp implementations
|
||
come with mature compilers that generate quite efficient machine
|
||
code. I'll talk in a moment about how to help these compilers
|
||
generate code that will be competitive with code generated by C
|
||
compilers, but these implementations already are quite a bit faster
|
||
than those of languages whose implementations are less mature and use
|
||
simpler compilers or interpreters. Also, since the Lisp compiler is
|
||
available at runtime, the Lisp programmer has some possibilities that
|
||
would be hard to emulate in other languages--your programs can
|
||
generate Lisp code at runtime that's then compiled into machine code
|
||
and run. If the generated code is going to run enough times, this can
|
||
be a big win. Or, even without using the compiler at runtime,
|
||
closures give you another way to meld machine code with runtime data.
|
||
For instance, the CL-PPCRE regular expression library, running in
|
||
CMUCL, is faster than Perl's regular expression engine on some
|
||
benchmarks, even though Perl's engine is written in highly tuned C.
|
||
This is presumably because in Perl a regular expression is translated
|
||
into what are essentially bytecodes that are then interpreted by the
|
||
regex engine, while CL-PPCRE translates a regular expression into a
|
||
tree of compiled closures that invoke each other via the normal
|
||
function-calling machinery.<SUP>5</SUP></P><P>However, even with the right algorithm and a high-quality compiler,
|
||
you may not get the raw speed you need. Then it's time to think about
|
||
profiling and tuning. The key, in Lisp as in any language, is to
|
||
profile first to find the spots where your program is actually
|
||
spending its time and then worry about speeding up those
|
||
parts.<SUP>6</SUP></P><P>You have a number of different ways to approach profiling. The
|
||
language standard provides a few rudimentary tools for measuring how
|
||
long certain forms take to execute. In particular, the <CODE><B>TIME</B></CODE>
|
||
macro can be wrapped around any form and will return whatever values
|
||
the form returns after printing a message to <CODE><B>*TRACE-OUTPUT*</B></CODE>
|
||
about how long it took to run and how much memory it used. The exact
|
||
form of the message is implementation defined.</P><P>You can use <CODE><B>TIME</B></CODE> for a bit of quick-and-dirty profiling to
|
||
narrow your search for bottlenecks. For instance, suppose you have a
|
||
function that's taking a long time to run and that calls two other
|
||
functions--something like this:</P><PRE>(defun foo ()
|
||
(bar)
|
||
(baz))</PRE><P>If you want to see whether <CODE>bar</CODE> or <CODE>baz</CODE> is taking more
|
||
time, you can change the definition of <CODE>foo</CODE> to this:</P><PRE>(defun foo ()
|
||
(time (bar))
|
||
(time (baz)))</PRE><P>Now you can call <CODE>foo</CODE>, and Lisp will print two reports, one for
|
||
<CODE>bar</CODE> and one for <CODE>baz</CODE>. The form is implementation
|
||
dependent; here's what it looks like in Allegro Common Lisp:</P><PRE>CL-USER> (foo)
|
||
; cpu time (non-gc) 60 msec user, 0 msec system
|
||
; cpu time (gc) 0 msec user, 0 msec system
|
||
; cpu time (total) 60 msec user, 0 msec system
|
||
; real time 105 msec
|
||
; space allocation:
|
||
; 24,172 cons cells, 1,696 other bytes, 0 static bytes
|
||
; cpu time (non-gc) 540 msec user, 10 msec system
|
||
; cpu time (gc) 170 msec user, 0 msec system
|
||
; cpu time (total) 710 msec user, 10 msec system
|
||
; real time 1,046 msec
|
||
; space allocation:
|
||
; 270,172 cons cells, 1,696 other bytes, 0 static bytes</PRE><P>Of course, that'd be a bit easier to read if the output included a
|
||
label. If you use this technique a lot, it might be worth defining
|
||
your own macro like this:</P><PRE>(defmacro labeled-time (form)
|
||
`(progn
|
||
(format *trace-output* "~2&~a" ',form)
|
||
(time ,form)))</PRE><P>If you replace <CODE><B>TIME</B></CODE> with <CODE>labeled-time</CODE> in <CODE>foo</CODE>,
|
||
you'll get this output:</P><PRE>CL-USER> (foo)
|
||
|
||
(BAR)
|
||
; cpu time (non-gc) 60 msec user, 0 msec system
|
||
; cpu time (gc) 0 msec user, 0 msec system
|
||
; cpu time (total) 60 msec user, 0 msec system
|
||
; real time 131 msec
|
||
; space allocation:
|
||
; 24,172 cons cells, 1,696 other bytes, 0 static bytes
|
||
|
||
(BAZ)
|
||
; cpu time (non-gc) 490 msec user, 0 msec system
|
||
; cpu time (gc) 190 msec user, 10 msec system
|
||
; cpu time (total) 680 msec user, 10 msec system
|
||
; real time 1,088 msec
|
||
; space allocation:
|
||
; 270,172 cons cells, 1,696 other bytes, 0 static bytes</PRE><P>From this output, it's clear that most of the time in <CODE>foo</CODE> is
|
||
spent in <CODE>baz</CODE>.</P><P>Of course, the output from <CODE><B>TIME</B></CODE> gets a bit unwieldy if the form
|
||
you want to profile is called repeatedly. You can build your own
|
||
measurement tools using the functions <CODE><B>GET-INTERNAL-REAL-TIME</B></CODE> and
|
||
<CODE><B>GET-INTERNAL-RUN-TIME</B></CODE>, which return a number that increases by
|
||
the value of the constant <CODE><B>INTERNAL-TIME-UNITS-PER-SECOND</B></CODE> each
|
||
second. <CODE><B>GET-INTERNAL-REAL-TIME</B></CODE> measures <I>wall time</I>, the
|
||
actual amount of time elapsed, while <CODE><B>GET-INTERNAL-RUN-TIME</B></CODE>
|
||
measures some implementation-defined value such as the amount of time
|
||
Lisp was actually executing or the time Lisp was executing user code
|
||
and not internal bookkeeping such as the garbage collector. Here's a
|
||
trivial but useful profiling tool built with a few macros and
|
||
<CODE><B>GET-INTERNAL-RUN-TIME</B></CODE>:</P><PRE>(defparameter *timing-data* ())
|
||
|
||
(defmacro with-timing (label &body body)
|
||
(with-gensyms (start)
|
||
`(let ((,start (get-internal-run-time)))
|
||
(unwind-protect (progn ,@body)
|
||
(push (list ',label ,start (get-internal-run-time)) *timing-data*)))))
|
||
|
||
(defun clear-timing-data ()
|
||
(setf *timing-data* ()))
|
||
|
||
(defun show-timing-data ()
|
||
(loop for (label time count time-per %-of-total) in (compile-timing-data) do
|
||
(format t "~3d% ~a: ~d ticks over ~d calls for ~d per.~%"
|
||
%-of-total label time count time-per)))
|
||
|
||
(defun compile-timing-data ()
|
||
(loop with timing-table = (make-hash-table)
|
||
with count-table = (make-hash-table)
|
||
for (label start end) in *timing-data*
|
||
for time = (- end start)
|
||
summing time into total
|
||
do
|
||
(incf (gethash label timing-table 0) time)
|
||
(incf (gethash label count-table 0))
|
||
finally
|
||
(return
|
||
(sort
|
||
(loop for label being the hash-keys in timing-table collect
|
||
(let ((time (gethash label timing-table))
|
||
(count (gethash label count-table)))
|
||
(list label time count (round (/ time count)) (round (* 100 (/ time total))))))
|
||
#'> :key #'fifth))))</PRE><P>This profiler lets you wrap a <CODE>with-timing</CODE> around any form;
|
||
each time the form is executed, the time it starts and the time it
|
||
ends are recorded, associating with a label you provide. The function
|
||
<CODE>show-timing-data</CODE> dumps out a table showing how much time was
|
||
spent in different labeled sections of code like this:</P><PRE>CL-USER> (show-timing-data)
|
||
84% BAR: 650 ticks over 2 calls for 325 per.
|
||
16% FOO: 120 ticks over 5 calls for 24 per.
|
||
NIL</PRE><P>You could obviously make this profiling code more sophisticated in
|
||
many ways. Alternatively, your Lisp implementation most likely
|
||
provides its own profiling tools, which, since they have access to
|
||
the internals of the implementation, can get at information not
|
||
necessarily available to user-level code.</P><P>Once you've found the bottleneck in your code, you can start tuning.
|
||
The first thing you should try, of course, is to find a more
|
||
efficient basic algorithm--that's where the big gains are to be had.
|
||
But assuming you're already using an appropriate algorithm, then it's
|
||
down to <I>code bumming</I>--locally optimizing the code so it does
|
||
absolutely no more work than necessary.</P><P>The main tools for code bumming in Common Lisp are its optional
|
||
declarations. The basic idea behind declarations in Common Lisp is
|
||
that they're used to give the compiler information it can use in a
|
||
variety of ways to generate better code.</P><P>For a simple example, consider this Common Lisp function:</P><PRE>(defun add (x y) (+ x y))</PRE><P>I mentioned in Chapter 10 that if you compare the performance of this
|
||
function Lisp to the seemingly equivalent C function:</P><PRE>int add (int x, int y) { return x + y; }</PRE><P>you'll likely find the Common Lisp version to be quite a bit slower,
|
||
even if your Common Lisp implementation features a high-quality native
|
||
compiler.</P><P>That's because the Common Lisp version is doing a lot more--the
|
||
Common Lisp compiler doesn't even know that the values of <CODE>a</CODE>
|
||
and <CODE>b</CODE> are numbers and so has to generate code to check at
|
||
runtime. And once it determines they <I>are</I> numbers, it has to
|
||
determine what types of numbers--integers, rationals, floating
|
||
point, or complex--and dispatch to the appropriate addition routine
|
||
for the actual types. And even if <CODE>a</CODE> and <CODE>b</CODE> are
|
||
integers--the case you care about--then the addition routine has to
|
||
account for the possibility that the result may be too large to
|
||
represent as a <I>fixnum</I>, a number that can be represented in a
|
||
single machine word, and thus it may have to allocate a <I>bignum</I>
|
||
object.</P><P>In C, on the other hand, because the type of all variables are
|
||
declared, the compiler knows exactly what kind of values <CODE>a</CODE> and
|
||
<CODE>b</CODE> will hold. And because C's arithmetic simply overflows when
|
||
the result of an addition is too large to represent in whatever type
|
||
is being returned, there's no checking for overflow and no allocation
|
||
of a bignum object to represent the result when the mathematical sum
|
||
is too large to fit in a machine word.</P><P>Thus, while the behavior of the Common Lisp code is much more likely
|
||
to be mathematically correct, the C version can probably be compiled
|
||
down to one or two machine instructions. But if you're willing to give
|
||
the Common Lisp compiler the same information the C compiler has about
|
||
the types of arguments and return values and to accept certain C-like
|
||
compromises in terms of generality and error checking, the Common
|
||
Lisp function can also be compiled down to an instruction or two.</P><P>That's what declarations are for. The main use of declarations is to
|
||
tell the compiler about the types of variables and other expressions.
|
||
For instance, you could tell the compiler that the arguments to
|
||
<CODE>add</CODE> are both fixnums by writing the function like this:</P><PRE>(defun add (x y)
|
||
(declare (fixnum x y))
|
||
(+ x y))</PRE><P>The <CODE><B>DECLARE</B></CODE> expression isn't a Lisp form; rather, it's part of
|
||
the syntax of the <CODE><B>DEFUN</B></CODE> and must appear before any other code in
|
||
the function body.<SUP>7</SUP> This declaration declares that the arguments
|
||
passed for the parameters <CODE>x</CODE> and <CODE>y</CODE> will always be
|
||
fixnums. In other words, it's a promise to the compiler, and the
|
||
compiler is allowed to generate code on the assumption that whatever
|
||
you tell it is true.</P><P>To declare the type of the value returned, you can wrap the form
|
||
<CODE>(+ x y)</CODE> in the <CODE><B>THE</B></CODE> special operator. This operator takes
|
||
a type specifier, such as <CODE><B>FIXNUM</B></CODE>, and a form and tells the
|
||
compiler the form will evaluate to the given type. Thus, to give the
|
||
Common Lisp compiler all the information about <CODE>add</CODE> that the C
|
||
compiler gets, you can write it like this:</P><PRE>(defun add (x y)
|
||
(declare (fixnum x y))
|
||
(the fixnum (+ x y)))</PRE><P>However, even this version needs one more declaration to give the
|
||
Common Lisp compiler the same license as the C compiler to generate
|
||
fast but dangerous code. The <CODE><B>OPTIMIZE</B></CODE> declaration is used to
|
||
tell the compiler how to balance five qualities: the speed of the
|
||
code generated; the amount of runtime error checking; the memory
|
||
usage of the code, both in terms of code size and runtime memory
|
||
usage; the amount of debugging information kept with the code; and
|
||
the speed of the compilation process. An <CODE><B>OPTIMIZE</B></CODE> declaration
|
||
consists of one or more lists, each containing one of the symbols
|
||
<CODE><B>SPEED</B></CODE>, <CODE><B>SAFETY</B></CODE>, <CODE><B>SPACE</B></CODE>, <CODE><B>DEBUG</B></CODE>, and
|
||
<CODE><B>COMPILATION-SPEED</B></CODE>, and a number from zero to three, inclusive.
|
||
The number specifies the relative weighting the compiler should give
|
||
to the corresponding quality, with <CODE>3</CODE> being the most important
|
||
and <CODE>0</CODE> meaning not important at all. Thus, to make Common Lisp
|
||
compile <CODE>add</CODE> more or less like a C compiler would, you can
|
||
write it like this:</P><PRE>(defun add (x y)
|
||
(declare (optimize (speed 3) (safety 0)))
|
||
(declare (fixnum x y))
|
||
(the fixnum (+ x y)))</PRE><P>Of course, now the Lisp version suffers from many of the same
|
||
liabilities as the C version--if the arguments passed aren't fixnums
|
||
or if the addition overflows, the result will be mathematically
|
||
incorrect or worse. Also, if someone calls <CODE>add</CODE> with a wrong
|
||
number of arguments, it may not be pretty. Thus, you should use these
|
||
kinds of declarations only after your program is working correctly.
|
||
And you should add them only where profiling shows they'll make a
|
||
difference. If you're getting reasonable performance without them,
|
||
leave them out. But when profiling shows you a real hot spot in your
|
||
code and you need to tune it up, go ahead. Because you can use
|
||
declarations this way, it's rarely necessary to rewrite code in C
|
||
just for performance reasons; FFIs are used to access existing C
|
||
code, but declarations are used when C-like performance is needed. Of
|
||
course, how close you can get the performance of a given piece of
|
||
Common Lisp code to C and C++ depends mostly on how much like C
|
||
you're willing to make it.</P><P>Another code-tuning tool built into Lisp is the function
|
||
<CODE><B>DISASSEMBLE</B></CODE>. 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.</P><P>Thus, you can use <CODE><B>DISASSEMBLE</B></CODE> 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 <CODE><B>DISASSEMBLE</B></CODE> to get a sense of the difference between the
|
||
first version of <CODE>add</CODE>, with no declarations, and the final
|
||
version. First, define and compile the original version.</P><PRE>(defun add (x y) (+ x y))</PRE><P>Then, at the REPL, call <CODE><B>DISASSEMBLE</B></CODE> with the name of the
|
||
function. In Allegro, it shows the following assembly-language-like
|
||
dump of the code generated by the compiler:</P><PRE>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</PRE><P>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 <CODE>add</CODE> with all the declarations.</P><PRE>(defun add (x y)
|
||
(declare (optimize (speed 3) (safety 0)))
|
||
(declare (fixnum x y))
|
||
(the fixnum (+ x y)))</PRE><P>Now disassemble <CODE>add</CODE> again, and see if the declarations had any
|
||
effect.</P><PRE>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</PRE><P>Looks like they did.</P><A NAME="delivering-applications"><H2>Delivering Applications</H2></A><P>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.</P><P>If you've written code you want to share with fellow Lisp
|
||
programmers, the most straightforward way to distribute it is as
|
||
source code.<SUP>8</SUP>
|
||
You can distribute a simple library as a single source file, which
|
||
programmers can <CODE><B>LOAD</B></CODE> into their Lisp image, possibly after
|
||
compiling it with <CODE><B>COMPILE-FILE</B></CODE>.</P><P>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 <CODE><B>DEFPACKAGE</B></CODE> forms must be loaded before any files
|
||
that use those packages can even be <CODE><B>READ</B></CODE>. Lispers call this the
|
||
<I>system definition</I> problem and typically handle it with tools
|
||
called <I>system definition facilities</I> or <I>system definition
|
||
utilities</I>, which are somewhat analogous to build tools such as
|
||
<CODE>make</CODE> or <CODE>ant</CODE>. As with <CODE>make</CODE> and <CODE>ant</CODE>, 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.</P><P>These days the most widely used system definition tool is ASDF, which
|
||
stands for <I>Another System Definition Facility</I>.<SUP>9</SUP>
|
||
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 <CODE>html.asd</CODE>, the ASD file for the
|
||
FOO library from Chapters 31 and 32:</P><PRE>(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))</PRE><P>If you add a symbolic link to this file from a directory listed in
|
||
<CODE>asdf:*central-registry*</CODE>,<SUP>10</SUP> then you can type this:</P><PRE>(asdf:operate 'asdf:load-op :html)</PRE><P>to compile and load the files <CODE>packages.lisp</CODE>, <CODE>html.lisp</CODE>,
|
||
and <CODE>html-macros.lisp</CODE> in the correct order after first making
|
||
sure the <CODE>:macro-utilities</CODE> 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.</P><P>Most free and open-source Common Lisp libraries you'll find will come
|
||
with an ASD file. Some will use other system definition tools such as
|
||
the slightly older MK:DEFSYSTEM or even utilities devised by the
|
||
library's author, but the tide seems to be turning in the direction
|
||
of ASDF.<SUP>11</SUP></P><P>Of course, while ASDF makes it easy for Lispers to install Lisp
|
||
libraries, it's not much help if you want to package an application
|
||
for an end user who doesn't know or care about Lisp. If you're
|
||
delivering a pure end-user application, presumably you want to
|
||
provide something the user can download, install, and run without
|
||
having to know anything about Lisp. You can't expect them to
|
||
separately download and install a Lisp implementation. And you want
|
||
them to be able to run your application just like any other
|
||
application--by double-clicking an icon on Windows or OS X or by
|
||
typing the name of the program at the command line on Unix.</P><P>However, unlike C programs, which can typically rely on certain
|
||
shared libraries (DLLs on Windows) that make up the C "runtime" being
|
||
present as part of the operating system, Lisp programs must include a
|
||
Lisp runtime, that is, the same program you run when you start Lisp
|
||
though perhaps with certain functionality not needed to run the
|
||
application excised.</P><P>To further complicate matters, <I>program</I> isn't really well defined
|
||
in Lisp. As you've seen throughout this book, the process of
|
||
developing software in Lisp is an incremental process that involves
|
||
making changes to the set of definitions and data living in your Lisp
|
||
image. The "program" is just a particular state of the image arrived
|
||
at by loading the <CODE>.lisp</CODE> or <CODE>.fasl</CODE> files that contain
|
||
code that creates the appropriate definitions and data. You could,
|
||
then, distribute a Lisp application as a Lisp runtime plus a bunch of
|
||
FASL files and an executable that starts the runtime, loads the
|
||
FASLs, and somehow invokes the appropriate starting function.
|
||
However, since actually loading the FASLs can take some time,
|
||
especially if they have to do any computation to set up the state of
|
||
the world, most Common Lisp implementations provide a way to <I>dump
|
||
an image</I>--to save the state of a running Lisp to a file called an
|
||
<I>image file</I> or sometimes a <I>core</I>. When a Lisp runtime starts,
|
||
the first thing it does is load an image file, which it can do in
|
||
much less time than it'd take to re-create the state by loading FASL
|
||
files.</P><P>Normally the image file is a default image containing only the
|
||
standard packages defined by the language and any extras provided by
|
||
the implementation. But with most implementations, you have a way to
|
||
specify a different image file. Thus, instead of packaging an app as
|
||
a Lisp runtime plus a bunch of FASLs, you can package it as a Lisp
|
||
runtime plus a single image file containing all the definitions and
|
||
data that make up your application. Then all you need is a program
|
||
that launches the Lisp runtime with the appropriate image file and
|
||
invokes whatever function serves as the entry point to the
|
||
application.</P><P>This is where things get implementation and operating-system
|
||
dependent. Some Common Lisp implementations, in particular the
|
||
commercial ones such as Allegro and LispWorks, provide tools for
|
||
building such an executable. For instance, Allegro's Enterprise
|
||
Edition provides a function <CODE>excl:generate-application</CODE> that
|
||
creates a directory containing the Lisp runtime as a shared library,
|
||
an image file, and an executable that starts the runtime with the
|
||
given image. Similarly, the LispWorks Professional Edition "delivery"
|
||
mechanism allows you to build single-file executables of your
|
||
programs. On Unix, with the various free and open-source
|
||
implementations, you can do essentially the same thing except it's
|
||
probably easier to use a shell script to start everything.</P><P>And on OS X things are even better--since all applications on OS X
|
||
are packaged as <CODE>.app</CODE> bundles, which are essentially
|
||
directories with a certain structure, it's not all that difficult to
|
||
package all the parts of a Lisp application as a double-clickable
|
||
<CODE>.app</CODE> bundle. Mikel Evins's Bosco tool makes it easy to create
|
||
<CODE>.app</CODE> bundles for applications running on OpenMCL.</P><P>Of course, another popular way to deliver applications these days is
|
||
as server-side applications. This is a niche where Common Lisp can
|
||
really excel--you can pick a combination of operating system and
|
||
Common Lisp implementation that works well for you, and you don't
|
||
have to worry about packaging the application to be installed by an
|
||
end user. And Common Lisp's interactive debugging and development
|
||
features make it possible to debug and upgrade a live server in ways
|
||
that either just aren't possible in a less dynamic language or would
|
||
require you to build a lot of specific infrastructure.</P><A NAME="where-to-go-next"><H2>Where to Go Next</H2></A><P>So, that's it. Welcome to the wonderful world of Lisp. The best thing
|
||
you can do now--if you haven't already--is to start writing your own
|
||
Lisp code. Pick a project that interests you, and do it in Common
|
||
Lisp. Then do another. Lather, rinse, repeat.</P><P>However, if you need some further pointers, this section offers some
|
||
places to go. For starters, check out the <I>Practical Common Lisp</I>
|
||
Web site at <CODE>http://www.gigamonkeys.com/book/</CODE>, where you can
|
||
find the source code from the practical chapters, errata, and links
|
||
to other Lisp resources on the Web.</P><P>In addition to the sites I mentioned in the "Finding Lisp Libraries"
|
||
section, you may also want explore the Common Lisp HyperSpec (a.k.a.
|
||
the HyperSpec or CLHS), an HTML version of the ANSI language standard
|
||
prepared by Kent Pitman and made available by LispWorks at
|
||
<CODE>http://www.lispworks.com/documentation/HyperSpec/index.html</CODE>.
|
||
The HyperSpec is by no means a tutorial, but it's as authoritative a
|
||
guide to the language as you can get without buying a printed copy of
|
||
the standard from ANSI and much more convenient for day-to-day
|
||
use.<SUP>12</SUP></P><P>If you want to get in touch with other Lispers, <CODE>comp.lang.lisp</CODE>
|
||
on Usenet and the <CODE>#lisp</CODE> IRC channel or the Freenode network
|
||
(<CODE>http://www.freenode.net</CODE>) are two of the main online hang-
|
||
outs. There are also a number of Lisp-related blogs, most of which
|
||
are aggregated on Planet Lisp at <CODE>http://planet.lisp.org/</CODE>.</P><P>And keep your eyes peeled in all those forums for announcements of
|
||
local Lisp users get-togethers in your area--in the past few years,
|
||
Lispnik gatherings have popped up in cities around the world, from
|
||
New York to Oakland, from Cologne to Munich, and from Geneva to
|
||
Helsinki.</P><P>If you want to stick to books, here are a few suggestions. For a nice
|
||
thick reference book to stick on your desk, grab <I>The ANSI Common
|
||
Lisp Reference Book</I> edited by David Margolies (Apress,
|
||
2005).<SUP>13</SUP></P><P>For more on Common Lisp's object system, you can start with
|
||
<I>Object-Oriented Programming in Common Lisp: A Programmer's Guide
|
||
to CLOS</I> by Sonya E. Keene (Addison-Wesley, 1989). Then if you really
|
||
want to become an object wizard or just to stretch your mind in
|
||
interesting ways, read <I>The Art of the Metaobject Protocol</I> by
|
||
Gregor Kiczales, Jim des Rivi<76>res, and Daniel G. Bobrow (MIT Press,
|
||
1991). This book, also known as AMOP, is both an explanation of what
|
||
a metaobject protocol is and why you want one and the de facto
|
||
standard for the metaobject protocol supported by many Common Lisp
|
||
implementations.</P><P>Two books that cover general Common Lisp technique are <I>Paradigms
|
||
of Artificial Intelligence Programming: Case Studies in Common Lisp</I>
|
||
by Peter Norvig (Morgan Kaufmann, 1992) and <I>On Lisp: Advanced
|
||
Techniques for Common Lisp</I> by Paul Graham (Prentice Hall, 1994). The
|
||
former provides a solid introduction to artificial intelligence
|
||
techniques while teaching quite a bit about how to write good Common
|
||
Lisp code, and the latter is especially good in its treatment of
|
||
macros.</P><P>If you're the kind of person who likes to know how things work down
|
||
to the bits, <I>Lisp in Small Pieces</I> by Christian Queinnec
|
||
(Cambridge University Press, 1996) provides a nice blend of
|
||
programming language theory and practical Lisp implementation
|
||
techniques. While it's primarily focused on Scheme rather than Common
|
||
Lisp, the same principles apply.</P><P>For folks who want a little more theoretical look at things--or who
|
||
just want to know what it's like to be a freshman comp sci student at
|
||
M.I.T.--<I>Structure and Interpretation of Computer Programs</I>, Second
|
||
Edition, by Harold Abelson, Gerald Jay Sussman, and Julie Sussman
|
||
(M.I.T. <I>Press, 1996) is a classic computer science text that uses
|
||
Scheme to teach important programming c</I>oncepts. Any programmer can
|
||
learn a lot from this book--just remember that there are important
|
||
differences between Scheme and Common Lisp.</P><P>Once you've wrapped your mind around Lisp, you may want to place it
|
||
in a bit of context. Since no one can claim to really understand
|
||
object orientation who doesn't know something about Smalltalk, you
|
||
might want to start with <I>Smalltalk-80: The Language</I> by Adele
|
||
Goldberg and David Robson (Addison Wesley, 1989), the standard
|
||
introduction to the core of Smalltalk. After that, <I>Smalltalk Best
|
||
Practice Patterns</I> by Kent Beck (Prentice Hall, 1997) is full of good
|
||
advice aimed at Smalltalkers, much of which is applicable to any
|
||
object-oriented language.</P><P>And at the other end of the spectrum, <I>Object-Oriented Software
|
||
Construction</I> by Bertrand Meyer (Prentice Hall, 1997) is an excellent
|
||
exposition of the static language mind-set from the inventor of
|
||
Eiffel, an oft-overlooked descendant of Simula and Algol. It contains
|
||
much food for thought, even for programmers working with dynamic
|
||
languages such as Common Lisp. In particular, Meyer's ideas about
|
||
Design By Contract can shed a lot of light on how one ought to use
|
||
Common Lisp's condition system.</P><P>Though not about computers per se, <I>The Wisdom of Crowds: Why the
|
||
Many Are Smarter Than the Few and How Collective Wisdom Shapes
|
||
Business, Economies, Societies, and Nations</I> by James Surowiecki
|
||
(Doubleday, 2004) contains an excellent answer to the question, "If
|
||
Lisp's so great how come everybody isn't using it?" See the section
|
||
on "Plank-Road Fever" starting on page 53.</P><P>And finally, for some fun, and to learn about the influence Lisp and
|
||
Lispers have had on hacker culture, dip into (or read from cover to
|
||
cover) <I>The New Hacker's Dictionary</I>, Third Edition, compiled by
|
||
Eric S. Raymond (MIT Press, 1996) and based on the original <I>The
|
||
Hacker's Dictionary</I> edited by Guy Steele (Harper & Row, 1983).</P><P>But don't let all these suggestions interfere with your
|
||
programming--the only way to really learn a language is to use it. If
|
||
you've made it this far, you're certainly ready to do that. Happy
|
||
hacking!
|
||
</P><HR/><DIV CLASS="notes"><P><SUP>1</SUP>The combination of Common Lisp's read-time
|
||
conditionalization and macros makes it quite feasible to develop
|
||
portability libraries that do nothing but provide a common API
|
||
layered over whatever API different implementations provide for
|
||
facilities not specified in the language standard. The portable
|
||
pathname library from Chapter 15 is an example of this kind of
|
||
library, albeit to smooth over differences in interpretation of the
|
||
standard rather than implementation-dependent APIs.</P><P><SUP>2</SUP>A Foreign Function
|
||
Interface is basically equivalent to JNI in Java, XS in Perl, or the
|
||
extension module API in Python.</P><P><SUP>3</SUP>As of this writing, the two main drawbacks of UFFI
|
||
are the lack of support for callbacks from C into Lisp, which many
|
||
but not all implementations' FFIs support, and the lack of support
|
||
for CLISP, whose FFI is quite good but different enough from the
|
||
others as to not fit easily into the UFFI model.</P><P><SUP>4</SUP>Knuth has used the saying several times in
|
||
publications, including in his 1974 ACM Turing Award paper, "Computer
|
||
Programming as an Art," and in his paper "Structured Programs with
|
||
goto Statements." In his paper "The Errors of TeX," he attributes the
|
||
saying to C.A.R. Hoare. And Hoare, in an 2004 e-mail to Hans Genwitz
|
||
of phobia.com, said he didn't remember the origin of the saying but
|
||
that he might have attributed it to Dijkstra.</P><P><SUP>5</SUP>CL-PPCRE also takes advantage of
|
||
another Common Lisp feature I haven't discussed, <I>compiler macros</I>.
|
||
A compiler macro is a special kind of macro that's given a chance to
|
||
optimize calls to a specific function by transforming calls to that
|
||
function into more efficient code. CL-PPCRE defines compiler macros
|
||
for its functions that take regular expression arguments. The
|
||
compiler macros optimize calls to those functions in which the
|
||
regular expression is a constant value by parsing the regular
|
||
expression at compile time rather than leaving it to be done at
|
||
runtime. Look up <CODE><B>DEFINE-COMPILER-MACRO</B></CODE> in your favorite Common
|
||
Lisp reference for more information about compiler macros.</P><P><SUP>6</SUP>The word <I>premature</I> in "premature optimization" can
|
||
pretty much be defined as "before profiling." Remember that even if
|
||
you can speed up a piece of code to the point where it takes
|
||
literally no time to run, you'll still speed up your program only by
|
||
whatever percentage of time it spent in that piece of code.</P><P><SUP>7</SUP>Declarations can appear in most forms that
|
||
introduce new variables, such as <CODE><B>LET</B></CODE>, <CODE><B>LET*</B></CODE>, and the <CODE><B>DO</B></CODE>
|
||
family of looping macros. <CODE><B>LOOP</B></CODE> has its own syntax for declaring
|
||
the types of loop variables. The special operator <CODE><B>LOCALLY</B></CODE>,
|
||
mentioned in Chapter 20, does nothing but create a scope in which you
|
||
can make declarations.</P><P><SUP>8</SUP>The FASL files produced by <CODE><B>COMPILE-FILE</B></CODE> 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 <CODE><B>LOAD</B></CODE>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.</P><P><SUP>9</SUP>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.</P><P><SUP>10</SUP>On Windows, where there are no
|
||
symbolic links, it works a little bit differently but roughly the
|
||
same.</P><P><SUP>11</SUP>Another tool, ASDF-INSTALL, builds on top of ASDF and
|
||
MK:DEFSYSTEM, providing an easy way to automatically download and
|
||
install libraries from the network. The best starting point for
|
||
learning about ASDF-INSTALL is Edi Weitz's "A tutorial for
|
||
ASDF-INSTALL" (<CODE>http:// www.weitz.de/asdf-install/</CODE>).</P><P><SUP>12</SUP>SLIME incorporates an Elisp library that allows you to
|
||
automatically jump to the HyperSpec entry for any name defined in the
|
||
standard. You can also download a complete copy of the HyperSpec to
|
||
keep locally for offline browsing.</P><P><SUP>13</SUP>Another classic reference is <I>Common Lisp: The
|
||
Language</I> by Guy Steele (Digital Press, 1984 and 1990). The first
|
||
edition, a.k.a. CLtL1, was the de facto standard for the language for
|
||
a number of years. While waiting for the official ANSI standard to be
|
||
finished, Guy Steele--who was on the ANSI committee--decided to
|
||
release a second edition to bridge the gap between CLtL1 and the
|
||
eventual standard. The second edition, now known as CLtL2, is
|
||
essentially a snapshot of the work of the standardization committee
|
||
taken at a particular moment in time near to, but not quite at, the
|
||
end of the standardization process. Consequently, CLtL2 differs from
|
||
the standard in ways that make it not a very good day-to-day
|
||
reference. It is, however, a useful historical document, particularly
|
||
because it includes documentation of some features that were dropped
|
||
from the standard before it was finished as well as commentary that
|
||
isn't part of the standard about why certain features are the way
|
||
they are.</P></DIV></BODY></HTML> |