This is pcl.info, produced by makeinfo version 5.2 from pcl.texi.
File: pcl.info, Node: Top, Next: Letter, Prev: (dir), Up: (dir)
Practical Common Lisp
=====================
Unofficial Texinfo Format (C) Peter Seibel
“that book is dead sexy” — ‘Xach on #lisp’
(*Note Blurbs::.)
This page, and the pages it links to, contain text of the Common Lisp
book Practical Common Lisp published by Apress
(https://www.apress.com/us/book/9781590592397). These pages now contain
the final text as it appears in the book. If you find errors in these
pages, please send email to <book@gigamonkeys.com>. These pages will
remain online in perpetuity -— I hope they will serve as a useful
introduction to Common Lisp for folks who are curious about Lisp but
maybe not yet curious enough to shell out big bucks for a dead-tree book
and a good Common Lisp tutorial for folks who want to get down to real
coding right away. However, don't let that stop you from buying the
printed version available from Apress at your favorite local or online
bookseller. For the complete bookstore browsing experience, you can
read the letter to the reader (*Note Letter::.) that appears on the
back cover of the treeware edition of the book.
* Menu:
* Letter:: Letter to the Reader
* Blurbs:: Blurbs
* Chapter 1:: Introduction: Why Lisp?
* Chapter 2:: Lather, Rinse, Repeat: A Tour of the REPL
* Chapter 3:: Practical: A Simple Database
* Chapter 4:: Syntax and Semantics
* Chapter 5:: Functions
* Chapter 6:: Variables
* Chapter 7:: Macros: Standard Control Constructs
* Chapter 8:: Macros: Defining Your Own
* Chapter 9:: Practical: Building a Unit Test Framework
* Chapter 10:: Numbers, Characters, and Strings
* Chapter 11:: Collections
* Chapter 12:: They Called It LISP for a Reason: List Processing
* Chapter 13:: Beyond Lists: Other Uses for Cons Cells
* Chapter 14:: Files and File I/O
* Chapter 15:: Practical: A Portable Pathname Library
* Chapter 16:: Object Reorientation: Generic Functions
* Chapter 17:: Object Reorientation: Classes
* Chapter 18:: A Few FORMAT Recipes
* Chapter 19:: Beyond Exception Handling: Conditions and Restarts
* Chapter 20:: The Special Operators
* Chapter 21:: Programming in the Large: Packages and Symbols
* Chapter 22:: LOOP for Black Belts
* Chapter 23:: Practical: A Spam Filter
* Chapter 24:: Practical: Parsing Binary Files
* Chapter 25:: Practical: An ID3 Parser
* Chapter 26:: Practical: Web Programming with AllegroServe
* Chapter 27:: Practical: An MP3 Database
* Chapter 28:: Practical: A Shoutcast Server
* Chapter 29:: Practical: An MP3 Browser
* Chapter 30:: Practical: An HTML Generation Library, the Interpreter
* Chapter 31:: Practical: An HTML Generation Library, the Compiler
* Chapter 32:: Conclusion: What's Next?
-- The Detailed Node Listing --
1. Introduction: Why Lisp?
* 1-1:: Why Lisp?
* 1-2:: Where It Began
* 1-2-1:: But I learned Lisp Once, And IT Wasn't Like what you're describing
* 1-3:: Who This Book Is For
2. Lather, Rinse, Repeat: A Tour of the REPL
* 2-1:: Choosing a Lisp Implementation
* 2-2:: Getting Up and Running with Lisp in a Box
* 2-3:: Free Your Mind: Interactive Programming
* 2-4:: Experimenting in the REPL
* 2-5:: "Hello, World," Lisp Style
* 2-6:: Saving Your Work
3. Practical: A Simple Database
* 3-1:: CDs and Records
* 3-2:: Filing CDs
* 3-3:: Looking at the Database Contents
* 3-4:: Improving the User Interaction
* 3-5:: Saving and Loading the Database
* 3-6:: Querying the Database
* 3-7:: Updating Existing Records-Another Use for WHERE
* 3-8:: Removing Duplication and Winning Big
* 3-9:: Wrapping Up
4. Syntax and Semantics
* 4-1:: What's with All the Parentheses?
* 4-2:: Breaking Open the Black Box
* 4-3:: S-expressions
* 4-4:: S-expressions As Lisp Forms
* 4-5:: Function Calls
* 4-6:: Special Operators
* 4-7:: Macros
* 4-8:: Truth, Falsehood, and Equality
* 4-9:: Formatting Lisp Code
5. Functions
* 5-1:: Defining New Functions
* 5-2:: Function Parameter Lists
* 5-3:: Optional Parameters
* 5-4:: Rest Parameters
* 5-5:: Keyword Parameters
* 5-6:: Mixing Different Parameter Types
* 5-7:: Function Return Values
* 5-8:: Functions As Data, a.k.a. Higher-Order Functions
* 5-9:: Anonymous Functions
6. Variables
* 6-1:: Variable Basics
* 6-2:: Lexical Variables and Closures
* 6-3:: Dynamic, a.k.a. Special, Variables
* 6-4:: Constants
* 6-5:: Assignment
* 6-6:: Generalized Assignment
* 6-7:: Other Ways to Modify Places
7. Macros: Standard Control Constructs
* 7-1:: WHEN and UNLESS
* 7-2:: COND
* 7-3:: AND, OR, and NOT
* 7-4:: Looping
* 7-5:: DOLIST and DOTIMES
* 7-6:: DO
* 7-7:: The Mighty LOOP
8. Macros: Defining Your Own
* 8-1:: The Story of Mac: A Just-So Story
* 8-2:: Macro Expansion Time vs. Runtime
* 8-3:: DEF MACRO
* 8-4:: A Sample Macro: do-primes
* 8-5:: Macro Parameters
* 8-6:: Generating the Expansion
* 8-7:: Plugging the Leaks
* 8-8:: Macro-Writing Macros
* 8-8-1:: Another classic macro-writing MACRO: ONCE-ONLY
* 8-9:: Beyond Simple Macros
9. Practical: Building a Unit Test Framework
* 9-1:: Two First Tries
* 9-2:: Refactoring
* 9-3:: Fixing the Return Value
* 9-4:: Better Result Reporting
* 9-5:: An Abstraction Emerges
* 9-6:: A Hierarchy of Tests
* 9-7:: Wrapping Up
10. Numbers, Characters, and Strings
* 10-1:: Numbers
* 10-2:: Numeric Literals
* 10-3:: Basic Math
* 10-4:: Numeric Comparisons
* 10-5:: Higher Math
* 10-6:: Characters
* 10-7:: Character Comparisons
* 10-8:: Strings
* 10-9:: String Comparisons
11. Collections
* 11-1:: Vectors
* 11-2:: Subtypes of Vector
* 11-3:: Vectors As Sequences
* 11-4:: Sequence Iterating Functions
* 11-5:: Higher-Order Function Variants
* 11-6:: Whole Sequence Manipulations
* 11-7:: Sorting and Merging
* 11-8:: Subsequence Manipulations
* 11-9:: Sequence Predicates
* 11-10:: Sequence Mapping Functions
* 11-11:: Hash Tables
* 11-12:: Hash Table Iteration
12. They Called It LISP for a Reason: List Processing
* 12-1:: "There Is No List"
* 12-2:: Functional Programming and Lists
* 12-3:: "Destructive" Operations
* 12-4:: Combining Recycling with Shared Structure
* 12-5:: List-Manipulation Functions
* 12-6:: Mapping
* 12-7:: Other Structures
13. Beyond Lists: Other Uses for Cons Cells
* 13-1:: Trees
* 13-2:: Sets
* 13-3:: Lookup Tables: Alists and Plists
* 13-4:: DESTRUCTURING-BIND
14. Files and File I/O
* 14-1:: Reading File Data
* 14-2:: Reading Binary Data
* 14-3:: Bulk Reads
* 14-4:: File Output
* 14-5:: Closing Files
* 14-6:: Filenames
* 14-6-1:: How We Got Here
* 14-7:: How Pathnames Represent Filenames
* 14-8:: Constructing New Pathnames
* 14-9:: Two Representations of Directory Names
* 14-10:: Interacting with the File System
* 14-11:: Other Kinds of I/O
15. Practical: A Portable Pathname Library
* 15-1:: The API
* 15-2:: *FEATURES* and Read-Time Conditionalization
* 15-2-1:: Packaging the Library
* 15-3:: Listing a Directory
* 15-4:: Testing a File's Existence
* 15-5:: Walking a Directory Tree
16. Object Reorientation: Generic Functions
* 16-1:: Generic Functions and Classes
* 16-2:: Generic Functions and Methods
* 16-3:: DEFGENERIC
* 16-4:: DEFMETHOD
* 16-5:: Method Combination
* 16-6:: The Standard Method Combination
* 16-7:: Other Method Combinations
* 16-8:: Multimethods
* 16-8-1:: Multimethods vs. Method Overloading
* 16-9:: To Be Continued . . .
17. Object Reorientation: Classes
* 17-1:: DEFCLASS
* 17-1-1:: What Are "User-Defined Classes"?
* 17-2:: Slot Specifiers
* 17-3:: Object Initialization
* 17-4:: Accessor Functions
* 17-5:: WITH-SLOTS and WITH-ACCESSORS
* 17-6:: Class-Allocated Slots
* 17-7:: Slots and Inheritance
* 17-8:: Multiple Inheritance
* 17-9:: Good Object-Oriented Design
18. A Few FORMAT Recipes
* 18-1:: The FORMAT Function
* 18-2:: FORMAT Directives
* 18-3:: Basic Formatting
* 18-4:: Character and Integer Directives
* 18-5:: Floating-Point Directives
* 18-6:: English-Language Directives
* 18-7:: Conditional Formatting
* 18-8:: Iteration
* 18-9:: Hop, Skip, Jump
* 18-10:: And More . . .
19. Beyond Exception Handling: Conditions and Restarts
* 19-1:: The Lisp Way
* 19-2:: Conditions
* 19-3:: Condition Handlers
* 19-3-1:: JAVA-STYLE EXCEPTON HANDLING
* 19-4:: Restarts
* 19-5:: Providing Multiple Restarts
* 19-6:: Other Uses for Conditions
* 19-6-1:: Writing Robust Software
20. The Special Operators
* 20-1:: Controlling Evaluation
* 20-2:: Manipulating the Lexical Environment
* 20-3:: Local Flow of Control
* 20-4:: Unwinding the Stack
* 20-5:: Multiple Values
* 20-6:: EVAL-WHEN
* 20-7:: Other Special Operators
21. Programming in the Large: Packages and Symbols
* 21-1:: How the Reader Uses Packages
* 21-2:: A Bit of Package and Symbol Vocabulary
* 21-3:: Three Standard Packages
* 21-4:: Defining Your Own Packages
* 21-5:: Packaging Reusable Libraries
* 21-6:: Importing Individual Names
* 21-7:: Packaging Mechanics
* 21-8:: Package Gotchas
22. LOOP for Black Belts
* 22-1:: The Parts of a LOOP
* 22-2:: Iteration Control
* 22-3:: Counting Loops
* 22-4:: Looping Over Collections and Packages
* 22-5:: Equals-Then Iteration
* 22-6:: Local Variables
* 22-7:: Destructuring Variables
* 22-8:: Value Accumulation
* 22-9:: Unconditional Execution
* 22-10:: Conditional Execution
* 22-11:: Setting Up and Tearing Down
* 22-12:: Termination Tests
* 22-13:: Putting It All Together
23. Practical: A Spam Filter
* 23-1:: The Heart of a Spam Filter
* 23-2:: Training the Filter
* 23-3:: Per-Word Statistics
* 23-4:: Combining Probabilities
* 23-5:: Inverse Chi Square
* 23-6:: Training the Filter
* 23-7:: Testing the Filter
* 23-8:: A Couple of Utility Functions
* 23-9:: Analyzing the Results
* 23-10:: What's Next
24. Practical: Parsing Binary Files
* 24-1:: Binary Files
* 24-2:: Binary Format Basics
* 24-3:: Strings in Binary Files
* 24-4:: Composite Structures
* 24-5:: Designing the Macros
* 24-6:: Making the Dream a Reality
* 24-7:: Reading Binary Objects
* 24-8:: Writing Binary Objects
* 24-9:: Adding Inheritance and Tagged Structures
* 24-10:: Keeping Track of Inherited Slots
* 24-11:: Tagged Structures
* 24-12:: Primitive Binary Types
* 24-13:: The Current Object Stack
25. Practical: An ID3 Parser
* 25-1:: Structure of an ID3v2 Tag
* 25-2:: Defining a Package
* 25-3:: Integer Types
* 25-4:: String Types
* 25-5:: ID3 Tag Header
* 25-6:: ID3 Frames
* 25-7:: Detecting Tag Padding
* 25-8:: Supporting Multiple Versions of ID3
* 25-9:: Versioned Frame Base Classes
* 25-10:: Versioned Concrete Frame Classes
* 25-11:: What Frames Do You Actually Need?
* 25-12:: Text Information Frames
* 25-13:: Comment Frames
* 25-14:: Extracting Information from an ID3 Tag
26. Practical: Web Programming with AllegroServe
* 26-1:: A 30-Second Intro to Server-Side Web Programming
* 26-2:: AllegroServe
* 26-3:: Generating Dynamic Content with AllegroServe
* 26-4:: Generating HTML
* 26-5:: HTML Macros
* 26-6:: Query Parameters
* 26-7:: Cookies
* 26-8:: A Small Application Framework
* 26-9:: The Implementation
27. Practical: An MP3 Database
* 27-1:: The Database
* 27-1-1:: The Package
* 27-2:: Defining a Schema
* 27-3:: Inserting Values
* 27-4:: Querying the Database
* 27-5:: Matching Functions
* 27-6:: Getting at the Results
* 27-7:: Other Database Operations
28. Practical: A Shoutcast Server
* 28-1:: The Shoutcast Protocol
* 28-2:: Song Sources
* 28-2-1:: The Package
* 28-3:: Implementing Shoutcast
29. Practical: An MP3 Browser
* 29-1:: Playlists
* 29-1-1:: The Package
* 29-2:: Playlists As Song Sources
* 29-3:: Manipulating the Playlist
* 29-4:: Query Parameter Types
* 29-5:: Boilerplate HTML
* 29-6:: The Browse Page
* 29-7:: The Playlist
* 29-8:: Finding a Playlist
* 29-9:: Running the App
30. Practical: An HTML Generation Library, the Interpreter
* 30-1:: Designing a Domain-Specific Language
* 30-2:: The FOO Language
* 30-3:: Character Escaping
* 30-3-1:: The Package
* 30-4:: Indenting Printer
* 30-5:: HTML Processor Interface
* 30-6:: The Pretty Printer Backend
* 30-6-1:: Using Conditions to Have Your Cake and Eat It Too
* 30-7:: The Basic Evaluation Rule
* 30-8:: What's Next?
31. Practical: An HTML Generation Library, the Compiler
* 31-1:: The Compiler
* 31-2:: FOO Special Operators
* 31-3:: FOO Macros
* 31-4:: The Public API
* 31-5:: The End of the Line
32. Conclusion: What's Next?
* 32-1:: Finding Lisp Libraries
* 32-2:: Interfacing with Other Languages
* 32-3:: Make It Work, Make It Right, Make It Fast
* 32-4:: Delivering Applications
* 32-5:: Where to Go Next
File: pcl.info, Node: Letter, Next: Blurbs, Prev: Top, Up: Top
Letter to the Reader
********************
Dear Reader,
‘Practical Common Lisp’ ... isn't that an oxymoron? If you're like
most programmers, you probably know something about Lisp -— from a
comp sci course in college or from learning enough Elisp to customize
Emacs a bit. Or maybe you just know someone who won't shut up about
Lisp, the greatest language ever. But you probably never figured you'd
see ‘practical’ and ‘Lisp’ in the same book title.
Yet, you're reading this; you must want to know more. Maybe you believe
learning Lisp will make you a better programmer in any language. Or
maybe you just want to know what those Lisp fanatics are yammering about
all the time. Or maybe you have learned some Lisp but haven't quite
made the leap to using it to write interesting software.
If any of those is true, this book is for you. Using Common Lisp, an
ANSI standardized, industrial-strength dialect of Lisp, I show you how
to write software that goes well beyond silly academic exercises or
trivial editor customizations. And I show you how Lisp -— even with
many of its features adopted by other languages—still has a few tricks
up its sleeve.
But unlike many Lisp books, this one doesn't just touch on a few of
Lisp's greatest features and then leave you on your own to actually use
them. I cover all the language features you'll need to write real
programs and devote well over a third of the book to developing
nontrivial software —- a statistical spam filter, a library for
parsing binary files, and a server for streaming MP3s over a network
complete with an online MP3 database and Web interface.
So turn the book over, open it up, and see for yourself how eminently
practical using the greatest language ever invented can be.
Sincerely,
Peter Seibel
File: pcl.info, Node: Blurbs, Next: Chapter 1, Prev: Letter, Up: Top
“I have been complimented many times and they always embarrass
me; I always feel that they have not said enough.” —Mark Twain
Blurbs
******
“that book is dead sexy” — ‘Xach on #lisp’
-------------------------------------------------------------
“Peter Seibel offers a fresh view of Lisp and its possibilities
for elegantly solving problems. In Practical Common Lisp, he gives
enough basic information to let you quickly see the power of the
functional language paradigm. He then dazzles you with examples
that seem almost magical in their simplicity and power. This read
is pure fun from start to finish.” - ‘Gary Pollice’, from Dr.
Dobb's Portal May-17-2006 article on the 2006 Jolt Awards
(http://www.drdobbs.com/joltawards/2006-jolt-awards/187900423?pgno=4)
-------------------------------------------------------------
“Peter Seibel's ‘Practical Common Lisp’ is just what the
title implies: an excellent introduction to Common Lisp for someone
who wants to dive in and start using the language for real work.
The book is very well written and is fun to read—at least for
those of us whose idea of fun extends to learning new programming
languages.
Rather than spending a lot of time on abstract discussion of Lisp's
place in the universe of programming lnaguages, Seibel dives right
in, guiding the reader through a series of programming examples of
increasing complexity. This approach places the most emphasis on
those parts of Common Lisp that skilled programmers use the most,
without getting bogged down in the odd corners of Common Lisp that
even the experts must look up in the manual. The result of
Seibel's example-driven approach is to give the reader an excellent
appreciation of the power of Common Lisp in building complex,
evolving software systems with a minimum of effort.
There are already many good books on Common Lisp that offer a more
abstract and comparative approach, but a good 'Here's how you do
it—and why' book, aimed at the working programmer, is a valuable
contribution, both to current Common Lisp users and those who
should be.” — ‘Scott E. Fahlman, Research Professor of
Computer Science, Carnegie Mellon Universit’
-------------------------------------------------------------
“This book shows the power of Lisp not only in the areas that it
has traditionally been noted for—such as developing a complete
unit test framework in only 26 lines of code—but also in new
areas such as parsing binary MP3 files, building a web application
for browsing a collection of songs, and streaming audio over the
web. Many readers will be surprised that Lisp allows you to do all
this with conciseness similar to scripting languages such as
Python, efficiency similar to C++, and unparalleled flexibility in
designing your own language extensions.” - ‘Peter Norvig,
Director of Search Quality, Google Inc; author of Paradigms of
Artificial Intelligence Programming: Case Studies in Common Lisp’
-------------------------------------------------------------
“I wish this book had already existed when I started learning
Lisp. It's not that there aren't other good books about (Common)
Lisp out there, but none of them has such a pragmatic, up-to-date
approach. And let's not forget that Peter covers topics like
pathnames or conditions and restarts which are completely ignored
in the rest of the Lisp literature.
If you're new to Lisp and want to dive right in don't hesitate to
buy this book. Once you've read it and worked with it you can
continue with the ‘classics’ like Graham, Norvig, Keene, or
Steele.” — ‘Edi Weitz, maintainer of the Common Lisp Cookbook
and author of CL-PPCRE regular expression library.’
-------------------------------------------------------------
“Two prehensile toes up!” - ‘Kenny Tilton, comp.lang.lisp
demon, reporting on behalf of his development team.’
-------------------------------------------------------------
“Experienced programmers learn best from examples and it is
delightful to see that Lisp is finally being served with Seibel's
example-rich tutorial text. Especially delightful is the fact that
this book includes so many examples that fall within the realm of
problems today's programmers might be called upon to tackle, such
as Web development and streaming media. - ‘Philip Greenspun,
author of Software Engineering for Internet Applications, MIT
Department of Electrical Engineering and Computer Science’
-------------------------------------------------------------
“‘Practical Common Lisp’ is an excellent book that covers the
breadth of the Common Lisp language and also demonstrates the
unique features of Common Lisp with real-world applications that
the reader can run and extend. This book not only shows what
Common Lisp is but also why every programmer should be familiar
with Lisp.” - ‘John Foderaro, Senior Scientist, Franz Inc.’
-------------------------------------------------------------
“The Maxima Project frequently gets queries from potential new
contributors who would like to learn Common Lisp. I am pleased to
finally have a book that I can recommend to them without
reservation. Peter Seibel's clear, direct style allows the reader
to quickly appreciate the power of Common Lisp. His many included
examples, which focus on contemporary programming problems,
demonstrate that Lisp is much more than an academic programming
language. ‘Practical Common Lisp’ is a welcome addition to the
literature.” — ‘James Amundson, Maxima Project Leader’
-------------------------------------------------------------
“I like the interspersed Practical chapters on 'real' and useful
programs. We need books of this kind telling the world that
crunching strings and numbers into trees or graphs is easily done
in Lisp. — ‘Professor Christian Queinnec, Universite Paris 6
(Pierre et Marie Curie)’
-------------------------------------------------------------
“One of the most important parts of learning a programming
language is learning its proper programming style. This is hard to
teach, but it can be painlessly absorbed from ‘Practical Common
Lisp’. Just reading the practical examples made me a better
programmer in any language.” - ‘Peter Scott, Lisp programmer’
-------------------------------------------------------------
“Finally, a Lisp book for the rest of us. If you want to learn
how to write a factorial function, this is not your book. Seibel
writes for the practical programmer, emphasizing the
engineer/artist over the scientist, subtly and gracefully implying
the power of the language while solving understandable real-world
problems.
In most chapters, the reading of the chapter feels just like the
experience of writing a program, starting with a little
understanding, then having that understanding grow, like building
the shoulders upon which you can then stand. When Seibel
introduced macros as an aside while building a test framework, I
was shocked at how such a simple example made me really 'get' them.
Narrative context is extremely powerful and the technical books
that use it are a cut above. Congrats!” — ‘Keith Irwin, Lisp
Programmer’
-------------------------------------------------------------
“While learning Lisp, one is often refered to the CL HyperSpec if
they do not know what a particular function does, however, I found
that I often did not 'get it' just reading the HyperSpec. When I
had a problem of this manner, I turned to ‘Practical Common
Lisp’ every single time—it is by far the most readable source
on the subject that shows you how to program, not just tell you.”
— ‘Philip Haddad, Lisp Programmer’
-------------------------------------------------------------
“With the IT world evolving at an ever increasing pace,
professionals need the most powerful tools available. This is why
Common Lisp—the most powerful, flexible, and stable programming
language ever—is seeing such a rise in popularity. ‘Practical
Common Lisp’ is the long-awaited book that will help you harness
the power of Common Lisp to tackle today's complex real world
problems.” — ‘Marc Battyani, author of CL-PDF,
CL-TYPESETTING, and mod_lisp.’
-------------------------------------------------------------
“Please don't assume Common Lisp is only useful for Databases,
Unit Test Frameworks, Spam Filters, ID3 Parsers, Web Programming,
Shoutcast Servers, HTML Generation Interpreters, and HTML
Generation Compilers just because these are the only things
happened to be implemented in the book ‘Practical Common Lisp’.
— ‘Tobias C. Rittweiler, Lisp Programmer’
-------------------------------------------------------------
“When I met Peter, who just started writing this book, I asked to
myself (not to him, of course) ‘why yet another book on Common
Lisp, when there are many nice introductory books?’ One year
later, I found a draft of the new book and recognized I was wrong.
This book is not ‘yet another’ one. The author focuses on
practical aspects rather than on technical details of the language.
When I first studied Lisp by reading an introductory book, I felt I
understood the language, but I also had an impression ‘so
what?’, meaning I had no idea about how to use it. In contrast,
this book leaps into a ‘PRACTICAL’ chapter after the first few
chapters that explain the very basic notions of the language. Then
the readers are expected to learn more about the language while
they are following the PRACTICAL projects, which are combined
together to form a product of a significant size. After reading
this book, the readers will feel themselves expert programmers on
Common Lisp since they have ‘finished’ a big project already.
I think Lisp is the only language that allows this type of
practical introduction. Peter makes use of this feature of the
language in building up a fancy introduction on Common Lisp.” —
‘Taiichi Yuasa, Professor, Department of Communications and
Computer Engineering, Kyoto University’
-------------------------------------------------------------
Have something to say about this book? Something nice? Want to
see it here? Send it along to <book@gigamonkeys.com>.
File: pcl.info, Node: Chapter 1, Next: Chapter 2, Prev: Blurbs, Up: Top
1. Introduction: Why Lisp?
==========================
If you think the greatest pleasure in programming comes from getting a
lot done with code that simply and clearly expresses your intention,
then programming in Common Lisp is likely to be about the most fun you
can have with a computer. You'll get more done, faster, using it than
you would using pretty much any other language.
That's a bold claim. Can I justify it? Not in a just a few pages in
this chapter-you're going to have to learn some Lisp and see for
yourself-thus the rest of this book. For now, let me start with some
anecdotal evidence, the story of my own road to Lisp. Then, in the next
section, I'll explain the payoff I think you'll get from learning Common
Lisp.
I'm one of what must be a fairly small number of second-generation Lisp
hackers. My father got his start in computers writing an operating
system in assembly for the machine he used to gather data for his
doctoral dissertation in physics. After running computer systems at
various physics labs, by the 1980s he had left physics altogether and
was working at a large pharmaceutical company. That company had a
project under way to develop software to model production processes in
its chemical plants-if you increase the size of this vessel, how does it
affect annual production? The original team, writing in FORTRAN, had
burned through half the money and almost all the time allotted to the
project with nothing to show for their efforts. This being the 1980s
and the middle of the artificial intelligence (AI) boom, Lisp was in the
air. So my dad-at that point not a Lisper-went to Carnegie Mellon
University (CMU) to talk to some of the folks working on what was to
become Common Lisp about whether Lisp might be a good language for this
project.
The CMU folks showed him some demos of stuff they were working on, and
he was convinced. He in turn convinced his bosses to let his team take
over the failing project and do it in Lisp. A year later, and using
only what was left of the original budget, his team delivered a working
application with features that the original team had given up any hope
of delivering. My dad credits his team's success to their decision to
use Lisp.
Now, that's just one anecdote. And maybe my dad is wrong about why they
succeeded. Or maybe Lisp was better only in comparison to other
languages of the day. These days we have lots of fancy new languages,
many of which have incorporated features from Lisp. Am I really saying
Lisp can offer you the same benefits today as it offered my dad in the
1980s? Read on.
Despite my father's best efforts, I didn't learn any Lisp in high
school. After a college career that didn't involve much programming in
any language, I was seduced by the Web and back into computers. I
worked first in Perl, learning enough to be dangerous while building an
online discussion forum for ‘Mother Jones’ magazine's Web site and
then moving to a Web shop, Organic Online, where I worked on big-for the
time-Web sites such as the one Nike put up during the 1996 Olympics.
Later I moved onto Java as an early developer at WebLogic, now part of
BEA. After WebLogic, I joined another startup where I was the lead
programmer on a team building a transactional messaging system in Java.
Along the way, my general interest in programming languages led me to
explore such mainstream languages as C, C++, and Python, as well as less
well-known ones such as Smalltalk, Eiffel, and Beta.
So I knew two languages inside and out and was familiar with another
half dozen. Eventually, however, I realized my interest in programming
languages was really rooted in the idea planted by my father's tales of
Lisp-that different languages really are different, and that, despite
the formal Turing equivalence of all programming languages, you really
can get more done more quickly in some languages than others and have
more fun doing it. Yet, ironically, I had never spent that much time
with Lisp itself. So, I started doing some Lisp hacking in my free
time. And whenever I did, it was exhilarating how quickly I was able to
go from idea to working code.
For example, one vacation, having a week or so to hack Lisp, I decided
to try writing a version of a program-a system for breeding genetic
algorithms to play the game of Go-that I had written early in my career
as a Java programmer. Even handicapped by my then rudimentary knowledge
of Common Lisp and having to look up even basic functions, it still felt
more productive than it would have been to rewrite the same program in
Java, even with several extra years of Java experience acquired since
writing the first version.
A similar experiment led to the library I'll discuss in Chapter 24.
Early in my time at WebLogic I had written a library, in Java, for
taking apart Java class files. It worked, but the code was a bit of a
mess and hard to modify or extend. I had tried several times, over the
years, to rewrite that library, thinking that with my ever-improving
Java chops I'd find some way to do it that didn't bog down in piles of
duplicated code. I never found a way. But when I tried to do it in
Common Lisp, it took me only two days, and I ended up not only with a
Java class file parser but with a general-purpose library for taking
apart any kind of binary file. You'll see how that library works in
Chapter 24 and use it in Chapter 25 to write a parser for the ID3 tags
embedded in MP3 files.
* Menu:
* 1-1:: Why Lisp?
* 1-2:: Where It Began
* 1-3:: Who This Book Is For
File: pcl.info, Node: 1-1, Next: 1-2, Prev: Chapter 1, Up: Chapter 1
Why Lisp?
=========
It's hard, in only a few pages of an introductory chapter, to explain
why users of a language like it, and it's even harder to make the case
for why you should invest your time in learning a certain language.
Personal history only gets us so far. Perhaps I like Lisp because of
some quirk in the way my brain is wired. It could even be genetic,
since my dad has it too. So before you dive into learning Lisp, it's
reasonable to want to know what the payoff is going to be.
For some languages, the payoff is relatively obvious. For instance, if
you want to write low-level code on Unix, you should learn C. Or if you
want to write certain kinds of cross-platform applications, you should
learn Java. And any of a number companies still use a lot of C++, so if
you want to get a job at one of them, you should learn C++.
For most languages, however, the payoff isn't so easily categorized; it
has to do with subjective criteria such as how it feels to use the
language. Perl advocates like to say that Perl "makes easy things easy
and hard things possible" and revel in the fact that, as the Perl motto
has it, "There's more than one way to do it." (1) Python's fans, on the
other hand, think Python is clean and simple and think Python code is
easier to understand because, as ‘their’ motto says, "There's only
one way to do it."
So, why Common Lisp? There's no immediately obvious payoff for adopting
Common Lisp the way there is for C, Java, and C++ (unless, of course,
you happen to own a Lisp Machine). The benefits of using Lisp have much
more to do with the experience of using it. I'll spend the rest of this
book showing you the specific features of Common Lisp and how to use
them so you can see for yourself what it's like. For now I'll try to
give you a sense of Lisp's philosophy.
The nearest thing Common Lisp has to a motto is the koan-like
description, "the programmable programming language." While cryptic,
that description gets at the root of the biggest advantage Common Lisp
still has over other languages. More than any other language, Common
Lisp follows the philosophy that what's good for the language's designer
is good for the language's users. Thus, when you're programming in
Common Lisp, you almost never find yourself wishing the language
supported some feature that would make your program easier to write,
because, as you'll see throughout this book, you can just add the
feature yourself.
Consequently, a Common Lisp program tends to provide a much clearer
mapping between your ideas about how the program works and the code you
actually write. Your ideas aren't obscured by boilerplate code and
endlessly repeated idioms. This makes your code easier to maintain
because you don't have to wade through reams of code every time you need
to make a change. Even systemic changes to a program's behavior can
often be achieved with relatively small changes to the actual code.
This also means you'll develop code more quickly; there's less code to
write, and you don't waste time thrashing around trying to find a clean
way to express yourself within the limitations of the language. (2)
Common Lisp is also an excellent language for exploratory programming-if
you don't know exactly how your program is going to work when you first
sit down to write it, Common Lisp provides several features to help you
develop your code incrementally and interactively.
For starters, the interactive read-eval-print loop, which I'll introduce
in the next chapter, lets you continually interact with your program as
you develop it. Write a new function. Test it. Change it. Try a
different approach. You never have to stop for a lengthy compilation
cycle. (3)
Other features that support a flowing, interactive programming style are
Lisp's dynamic typing and the Common Lisp condition system. Because of
the former, you spend less time convincing the compiler you should be
allowed to run your code and more time actually running it and working
on it, (4) and the latter lets you develop even your error handling code
interactively.
Another consequence of being "a programmable programming language" is
that Common Lisp, in addition to incorporating small changes that make
particular programs easier to write, can easily adopt big new ideas
about how programming languages should work. For instance, the original
implementation of the Common Lisp Object System (CLOS), Common Lisp's
powerful object system, was as a library written in portable Common
Lisp. This allowed Lisp programmers to gain actual experience with the
facilities it provided before it was officially incorporated into the
language.
Whatever new paradigm comes down the pike next, it's extremely likely
that Common Lisp will be able to absorb it without requiring any changes
to the core language. For example, a Lisper has recently written a
library, AspectL, that adds support for aspect-oriented programming
(AOP) to Common Lisp. (5) If AOP turns out to be the next big thing,
Common Lisp will be able to support it without any changes to the base
language and without extra preprocessors and extra compilers. (6)
---------- Footnotes ----------
(1) Perl is also worth learning as "the duct tape of the Internet."
(2) Unfortunately, there's little actual research on the productivity
of different languages. One report that shows Lisp coming out well
compared to C++ and Java in the combination of programmer and program
efficiency is discussed at http://www.norvig.com/java-lisp.html.
(3) Psychologists have identified a state of mind called flow in
which we're capable of incredible concentration and productivity. The
importance of flow to programming has been recognized for nearly two
decades since it was discussed in the classic book about human factors
in programming ‘Peopleware: Productive Projects and Teams’ by Tom
DeMarco and Timothy Lister (Dorset House, 1987). The two key facts
about flow are that it takes around 15 minutes to get into a state of
flow and that even brief interruptions can break you right out of it,
requiring another 15-minute immersion to reenter. DeMarco and Lister,
like most subsequent authors, concerned themselves mostly with
flow-destroying interruptions such as ringing telephones and inopportune
visits from the boss. Less frequently considered but probably just as
important to programmers are the interruptions caused by our tools.
Languages that require, for instance, a lengthy compilation before you
can try your latest code can be just as inimical to flow as a noisy
phone or a nosy boss. So, one way to look at Lisp is as a language
designed to keep you in a state of flow.
(4) This point is bound to be somewhat controversial, at least with
some folks. Static versus dynamic typing is one of the classic
religious wars in programming. If you're coming from C++ and Java (or
from statically typed functional languages such as Haskel and ML) and
refuse to consider living without static type checks, you might as well
put this book down now. However, before you do, you might first want to
check out what self-described "statically typed bigot" Robert Martin
(author of ‘Designing Object Oriented C++ Applications Using the Booch
Method’ [Prentice Hall, 1995]) and C++ and Java author Bruce Eckel
(author of ‘Thinking in C++’ [Prentice Hall, 1995] and ‘Thinking
in Java’ [Prentice Hall, 1998]) have had to say about dynamic typing
on their weblogs (http://www.artima.com/weblogs/viewpost.jsp?thread=4639
and http://www.mindview.net/WebLog/log-0025). On the other hand, folks
coming from Smalltalk, Python, Perl, or Ruby should feel right at home
with this aspect of Common Lisp.
(5) AspectL is an interesting project insofar as AspectJ, its
Java-based predecessor, was written by Gregor Kiczales, one of the
designers of Common Lisp's object and metaobject systems. To many
Lispers, AspectJ seems like Kiczales's attempt to backport his ideas
from Common Lisp into Java. However, Pascal Costanza, the author of
AspectL, thinks there are interesting ideas in AOP that could be useful
in Common Lisp. Of course, the reason he's able to implement AspectL as
a library is because of the incredible flexibility of the Common Lisp
Meta Object Protocol Kiczales designed. To implement AspectJ, Kiczales
had to write what was essentially a separate compiler that compiles a
new language into Java source code. The AspectL project page is at
http://common-lisp.net/ project/aspectl/.
(6) Or to look at it another, more technically accurate, way, Common
Lisp comes with a built-in facility for integrating compilers for
embedded languages.
File: pcl.info, Node: 1-2, Next: 1-3, Prev: 1-1, Up: Chapter 1
Where It Began
==============
Common Lisp is the modern descendant of the Lisp language first
conceived by John McCarthy in 1956. Lisp circa 1956 was designed for
"symbolic data processing" (1) and derived its name from one of the
things it was quite good at: LISt Processing. We've come a long way
since then: Common Lisp sports as fine an array of modern data types as
you can ask for: a condition system that, as you'll see in Chapter 19,
provides a whole level of flexibility missing from the exception systems
of languages such as Java, Python, and C++; powerful facilities for
doing object-oriented programming; and several language facilities that
just don't exist in other programming languages. How is this possible?
What on Earth would provoke the evolution of such a well-equipped
language?
Well, McCarthy was (and still is) an artificial intelligence (AI)
researcher, and many of the features he built into his initial version
of the language made it an excellent language for AI programming.
During the AI boom of the 1980s, Lisp remained a favorite tool for
programmers writing software to solve hard problems such as automated
theorem proving, planning and scheduling, and computer vision. These
were problems that required a lot of hard-to-write software; to make a
dent in them, AI programmers needed a powerful language, and they grew
Lisp into the language they needed. And the Cold War helped-as the
Pentagon poured money into the Defense Advanced Research Projects Agency
(DARPA), a lot of it went to folks working on problems such as
large-scale battlefield simulations, automated planning, and natural
language interfaces. These folks also used Lisp and continued pushing
it to do what they needed.
The same forces that drove Lisp's feature evolution also pushed the
envelope along other dimensions-big AI problems eat up a lot of
computing resources however you code them, and if you run Moore's law in
reverse for 20 years, you can imagine how scarce computing resources
were on circa-80s hardware. The Lisp guys had to find all kinds of ways
to squeeze performance out of their implementations. Modern Common Lisp
implementations are the heirs to those early efforts and often include
quite sophisticated, native machine code-generating compilers. While
today, thanks to Moore's law, it's possible to get usable performance
from a purely interpreted language, that's no longer an issue for Common
Lisp. As I'll show in Chapter 32, with proper (optional) declarations,
a good Lisp compiler can generate machine code quite similar to what
might be generated by a C compiler.
The 1980s were also the era of the Lisp Machines, with several
companies, most famously Symbolics, producing computers that ran Lisp
natively from the chips up. Thus, Lisp became a systems programming
language, used for writing the operating system, editors, compilers, and
pretty much everything else that ran on the Lisp Machines.
In fact, by the early 1980s, with various AI labs and the Lisp machine
vendors all providing their own Lisp implementations, there was such a
proliferation of Lisp systems and dialects that the folks at DARPA began
to express concern about the Lisp community splintering. To address
this concern, a grassroots group of Lisp hackers got together in 1981
and began the process of standardizing a new language called Common Lisp
that combined the best features from the existing Lisp dialects. Their
work was documented in the book ‘Common Lisp the Language’ by Guy
Steele (Digital Press, 1984)-CLtL to the Lisp-cognoscenti.
By 1986 the first Common Lisp implementations were available, and the
writing was on the wall for the dialects it was intended to replace. In
1996, the American National Standards Institute (ANSI) released a
standard for Common Lisp that built on and extended the language
specified in CLtL, adding some major new features such as the CLOS and
the condition system. And even that wasn't the last word: like CLtL
before it, the ANSI standard intentionally leaves room for implementers
to experiment with the best way to do things: a full Lisp implementation
provides a rich runtime environment with access to GUI widgets, multiple
threads of control, TCP/IP sockets, and more. These days Common Lisp is
evolving much like other open-source languages-the folks who use it
write the libraries they need and often make them available to others.
In the last few years, in particular, there has been a spurt of activity
in open-source Lisp libraries.
So, on one hand, Lisp is one of computer science's "classical"
languages, based on ideas that have stood the test of time. (2) On the
other, it's a thoroughly modern, general-purpose language whose design
reflects a deeply pragmatic approach to solving real problems as
efficiently and robustly as possible. The only downside of Lisp's
"classical" heritage is that lots of folks are still walking around with
ideas about Lisp based on some particular flavor of Lisp they were
exposed to at some particular time in the nearly half century since
McCarthy invented Lisp. If someone tells you Lisp is only interpreted,
that it's slow, or that you have to use recursion for everything, ask
them what dialect of Lisp they're talking about and whether people were
wearing bell-bottoms when they learned it. (3)
* Menu:
* 1-2-1:: But I learned Lisp Once, And IT Wasn't Like what you're describing
---------- Footnotes ----------
(1) ‘Lisp 1.5 Programmer's Manual’ (M.I.T. Press, 1962)
(2) Ideas first introduced in Lisp include the if/then/else
construct, recursive function calls, dynamic memory allocation, garbage
collection, first-class functions, lexical closures, interactive
programming, incremental compilation, and dynamic typing.
(3) One of the most commonly repeated myths about Lisp is that it's
"dead." While it's true that Common Lisp isn't as widely used as, say,
Visual Basic or Java, it seems strange to describe a language that
continues to be used for new development and that continues to attract
new users as "dead." Some recent Lisp success stories include Paul
Graham's Viaweb, which became Yahoo Store when Yahoo bought his company;
ITA Software's airfare pricing and shopping system, QPX, used by the
online ticket seller Orbitz and others; Naughty Dog's game for the
PlayStation 2, Jak and Daxter, which is largely written in a
domain-specific Lisp dialect Naughty Dog invented called GOAL, whose
compiler is itself written in Common Lisp; and the Roomba, the
autonomous robotic vacuum cleaner, whose software is written in L, a
downwardly compatible subset of Common Lisp. Perhaps even more telling
is the growth of the Common-Lisp.net Web site, which hosts open-source
Common Lisp projects, and the number of local Lisp user groups that have
sprung up in the past couple of years.
File: pcl.info, Node: 1-2-1, Next: 1-3, Prev: 1-2, Up: 1-2
But I learned Lisp Once, And IT Wasn't Like what you're describing
==================================================================
If you've used Lisp in the past, you may have ideas about what "Lisp" is
that have little to do with Common Lisp. While Common Lisp supplanted
most of the dialects it's descended from, it isn't the only remaining
Lisp dialect, and depending on where and when you were exposed to Lisp,
you may very well have learned one of these other dialects.
Other than Common Lisp, the one general-purpose Lisp dialect that still
has an active user community is Scheme. Common Lisp borrowed a few
important features from Scheme but never intended to replace it.
Originally designed at M.I.T., where it was quickly put to use as a
teaching language for undergraduate computer science courses, Scheme has
always been aimed at a different language niche than Common Lisp. In
particular, Scheme's designers have focused on keeping the core language
as small and as simple as possible. This has obvious benefits for a
teaching language and also for programming language researchers who like
to be able to formally prove things about languages.
It also has the benefit of making it relatively easy to understand the
whole language as specified in the standard. But, it does so at the
cost of omitting many useful features that are standardized in Common
Lisp. Individual Scheme implementations may provide these features in
implementation-specific ways, but their omission from the standard makes
it harder to write portable Scheme code than to write portable Common
Lisp code.
Scheme also emphasizes a functional programming style and the use of
recursion much more than Common Lisp does. If you studied Lisp in
college and came away with the impression that it was only an academic
language with no real-world application, chances are you learned Scheme.
This isn't to say that's a particularly fair characterization of Scheme,
but it's even less applicable to Common Lisp, which was expressly
designed to be a real-world engineering language rather than a
theoretically "pure" language.
If you've learned Scheme, you should also be aware that a number of
subtle differences between Scheme and Common Lisp may trip you up.
These differences are also the basis for several perennial religious
wars between the hotheads in the Common Lisp and Scheme communities.
I'll try to point out some of the more important differences as we go
along.
Two other Lisp dialects still in widespread use are Elisp, the extension
language for the Emacs editor, and Autolisp, the extension language for
Autodesk's AutoCAD computer-aided design tool. Although it's possible
more lines of Elisp and Autolisp have been written than of any other
dialect of Lisp, neither can be used outside their host application, and
both are quite old-fashioned Lisps compared to either Scheme or Common
Lisp. If you've used one of these dialects, prepare to hop in the Lisp
time machine and jump forward several decades.
File: pcl.info, Node: 1-3, Next: Chapter 2, Prev: 1-2, Up: Chapter 1
Who This Book Is For
====================
This book is for you if you're curious about Common Lisp, regardless of
whether you're already convinced you want to use it or if you just want
to know what all the fuss is about.
If you've learned some Lisp already but have had trouble making the leap
from academic exercises to real programs, this book should get you on
your way. On the other hand, you don't have to be already convinced
that you want to use Lisp to get something out of this book.
If you're a hard-nosed pragmatist who wants to know what advantages
Common Lisp has over languages such as Perl, Python, Java, C, or C#,
this book should give you some ideas. Or maybe you don't even care
about using Lisp-maybe you're already sure Lisp isn't really any better
than other languages you know but are annoyed by some Lisper telling you
that's because you just don't "get it." If so, this book will give you
a straight-to-the-point introduction to Common Lisp. If, after reading
this book, you still think Common Lisp is no better than your current
favorite languages, you'll be in an excellent position to explain
exactly why.
I cover not only the syntax and semantics of the language but also how
you can use it to write software that does useful stuff. In the first
part of the book, I'll cover the language itself, mixing in a few
"practical" chapters, where I'll show you how to write real code. Then,
after I've covered most of the language, including several parts that
other books leave for you to figure out on your own, the remainder of
the book consists of nine more practical chapters where I'll help you
write several medium-sized programs that actually do things you might
find useful: filter spam, parse binary files, catalog MP3s, stream MP3s
over a network, and provide a Web interface for the MP3 catalog and
server.
After you finish this book, you'll be familiar with all the most
important features of the language and how they fit together, you'll
have used Common Lisp to write several nontrivial programs, and you'll
be well prepared to continue exploring the language on your own. While
everyone's road to Lisp is different, I hope this book will help smooth
the way for you. So, let's begin.
File: pcl.info, Node: Chapter 2, Prev: Chapter 1, Up: Top
2. Lather, Rinse, Repeat: A Tour of the REPL
============================================
In this chapter you'll set up your programming environment and write
your first Common Lisp programs. We'll use the easy-to-install Lisp in
a Box developed by Matthew Danish and Mikel Evins, which packages a
Common Lisp implementation with Emacs, a powerful Lisp-aware text
editor, and SLIME (1) , a Common Lisp development environment built on
top of Emacs.
This combo provides a state-of-the-art Common Lisp development
environment that supports the incremental, interactive development style
that characterizes Lisp programming. The SLIME environment has the
added advantage of providing a fairly uniform user interface regardless
of the operating system and Common Lisp implementation you choose. I'll
use the Lisp in a Box environment in order to have a specific
development environment to talk about; folks who want to explore other
development environments such as the graphical integrated development
environments (IDEs) provided by some of the commercial Lisp vendors or
environments based on other editors shouldn't have too much trouble
translating the basics (2) .
* Menu:
* 2-1:: Choosing a Lisp Implementation
* 2-2:: Getting Up and Running with Lisp in a Box
* 2-3:: Free Your Mind: Interactive Programming
* 2-4:: Experimenting in the REPL
* 2-5:: "Hello, World," Lisp Style
* 2-6:: Saving Your Work
---------- Footnotes ----------
(1) Superior Lisp Interaction Mode for Emacs
(2) If you've had a bad experience with Emacs previously, you should
treat Lisp in a Box as an IDE that happens to use an Emacs-like editor
as its text editor; there will be no need to become an Emacs guru to
program Lisp. It is, however, orders of magnitude more enjoyable to
program Lisp with an editor that has some basic Lisp awareness. At a
minimum, you'll want an editor that can automatically match ()s for you
and knows how to automatically indent Lisp code. Because Emacs is
itself largely written in a Lisp dialect, Elisp, it has quite a bit of
support for editing Lisp code. Emacs is also deeply embedded into the
history of Lisp and the culture of Lisp hackers: the original Emacs and
its immediate predecessors, TECMACS and TMACS, were written by Lispers
at the Massachusetts Institute of Technology (MIT). The editors on the
Lisp Machines were versions of Emacs written entirely in Lisp. The
first two Lisp Machine Emacs, following the hacker tradition of
recursive acronyms, were EINE and ZWEI, which stood for EINE Is Not
Emacs and ZWEI Was EINE Initially. Later ones used a descendant of
ZWEI, named, more prosaically, ZMACS.
File: pcl.info, Node: 2-1, Next: 2-2, Prev: Chapter 2, Up: Chapter 2
Choosing a Lisp Implementation
==============================
The first thing you have to do is to choose a Lisp implementation. This
may seem like a strange thing to have to do for folks used to languages
such as Perl, Python, Visual Basic (VB), C#, and Java. The difference
between Common Lisp and these languages is that Common Lisp is defined
by its standard-there is neither a single implementation controlled by a
benevolent dictator, as with Perl and Python, nor a canonical
implementation controlled by a single company, as with VB, C#, and Java.
Anyone who wants to read the standard and implement the language is free
to do so. Furthermore, changes to the standard have to be made in
accordance with a process controlled by the standards body American
National Standards Institute (ANSI). That process is designed to keep
any one entity, such as a single vendor, from being able to arbitrarily
change the standard (1) . Thus, the Common Lisp standard is a contract
between any Common Lisp vendor and Common Lisp programmers. The
contract tells you that if you write a program that uses the features of
the language the way they're described in the standard, you can count on
your program behaving the same in any conforming implementation.
On the other hand, the standard may not cover everything you may want to
do in your programs-some things were intentionally left unspecified in
order to allow continuing experimentation by implementers in areas where
there wasn't consensus about the best way for the language to support
certain features. So every implementation offers some features above
and beyond what's specified in the standard. Depending on what kind of
programming you're going to be doing, it may make sense to just pick one
implementation that has the extra features you need and use that. On
the other hand, if we're delivering Lisp source to be used by others,
such as libraries, you'll want-as far as possible-to write portable
Common Lisp. For writing code that should be mostly portable but that
needs facilities not defined by the standard, Common Lisp provides a
flexible way to write code "conditionalized" on the features available
in a particular implementation. You'll see an example of this kind of
code in Chapter 15 when we develop a simple library that smoothes over
some differences between how different Lisp implementations deal with
filenames.
For the moment, however, the most important characteristic of an
implementation is whether it runs on our favorite operating system. The
folks at Franz, makers of Allegro Common Lisp, are making available a
trial version of their product for use with this book that runs on
Linux, Windows, and OS X. Folks looking for an open-source
implementation have several options. SBCL (2) is a high-quality
open-source implementation that compiles to native code and runs on a
wide variety of Unixes, including Linux and OS X. SBCL is derived from
CMUCL (3) , which is a Common Lisp developed at Carnegie Mellon
University, and, like CMUCL, is largely in the public domain, except a
few sections licensed under Berkeley Software Distribution (BSD) style
licenses. CMUCL itself is another fine choice, though SBCL tends to be
easier to install and now supports 21-bit Unicode (4) . For OS X users,
OpenMCL is an excellent choice-it compiles to machine code, supports
threads, and has quite good integration with OS X's Carbon and Cocoa
toolkits. Other open-source and commercial implementations are
available. See Chapter 32 for resources from which you can get more
information.
All the Lisp code in this book should work in any conforming Common Lisp
implementation unless otherwise noted, and SLIME will smooth out some of
the differences between implementations by providing us with a common
interface for interacting with Lisp. The output shown in this book is
from Allegro running on GNU/Linux; in some cases, other Lisp's may
generate slightly different error messages or debugger output.
---------- Footnotes ----------
(1) Practically speaking, there's very little likelihood of the
language standard itself being revised-while there are a small handful
of warts that folks might like to clean up, the ANSI process isn't
amenable to opening an existing standard for minor tweaks, and none of
the warts that might be cleaned up actually cause anyone any serious
difficulty. The future of Common Lisp standardization is likely to
proceed via de facto standards, much like the "standardization" of Perl
and Python-as different implementers experiment with application
programming interfaces (APIs) and libraries for doing things not
specified in the language standard, other implementers may adopt them or
people will develop portability libraries to smooth over the differences
between implementations for features not specified in the language
standard.
(2) Steel Bank Common Lisp
(3) CMU Common Lisp
(4) SBCL forked from CMUCL in order to focus on cleaning up the
internals and making it easier to maintain. But the fork has been
amiable; bug fixes tend to propagate between the two projects, and
there's talk that someday they will merge back together.
File: pcl.info, Node: 2-2, Next: 2-3, Prev: 2-1, Up: Chapter 2
Getting Up and Running with Lisp in a Box
=========================================
Since the Lisp in a Box packaging is designed to get new Lispers up and
running in a first-rate Lisp development environment with minimum
hassle, all you need to do to get it running is to grab the appropriate
package for your operating system and the preferred Lisp from the Lisp
in a Box Web site listed in Chapter 32 and then follow the installation
instructions.
Since Lisp in a Box uses Emacs as its editor, you'll need to know at
least a bit about how to use it. Perhaps the best way to get started
with Emacs is to work through its built-in tutorial. To start the
tutorial, select the first item of the Help menu, Emacs tutorial. Or
press the Ctrl key, type h, release the Ctrl key, and then press t.
Most Emacs commands are accessible via such key combinations; because
key combinations are so common, Emacs users have a notation for
describing key combinations that avoids having to constantly write out
combinations such as "Press the Ctrl key, type h, release the Ctrl key,
and then press t." Keys to be pressed together-a so-called key
chord-are written together and separated by a hyphen. Keys, or key
chords, to be pressed in sequence are separated by spaces. In a key
chord, C represents the Ctrl key and M represents the Meta key (also
known as Alt). Thus, we could write the key combination we just
described that starts the tutorial like so: C-h t.
The tutorial describes other useful commands and the key combinations
that invoke them. Emacs also comes with extensive online documentation
using its own built-in hypertext documentation browser, Info. To read
the manual, type C-h i. The Info system comes with its own tutorial,
accessible simply by pressing h while reading the manual. Finally,
Emacs provides quite a few ways to get help, all bound to key combos
starting with C-h. Typing C-h ? brings up a complete list. Two of the
most useful, besides the tutorial, are C-h k, which lets us type any key
combo and tells us what command it invokes, and C-h w, which lets us
enter the name of a command and tells us what key combination invokes
it.
The other crucial bit of Emacs terminology, for folks who refuse to work
through the tutorial, is the notion of a ‘buffer’. While working in
Emacs, each file you edit will be represented by a different buffer,
only one of which is "current" at any given time. The current buffer
receives all input-whatever you type and any commands you invoke.
Buffers are also used to represent interactions with programs such as
Common Lisp. Thus, one common action you'll take is to "switch
buffers," which means to make a different buffer the current buffer so
you can edit a particular file or interact with a particular program.
The command ‘switch-to-buffer’, bound to the key combination C-x b,
prompts for the name of a buffer in the area at the bottom of the Emacs
frame. When entering a buffer name, hitting Tab will complete the name
based on the characters typed so far or will show a list of possible
completions. The prompt also suggests a default buffer, which you can
accept just by hitting Return. You can also switch buffers by selecting
a buffer from the Buffers menu.
In certain contexts, other key combinations may be available for
switching to certain buffers. For instance, when editing Lisp source
files, the key combo C-c C-z switches to the buffer where you interact
with Lisp.
File: pcl.info, Node: 2-3, Next: 2-4, Prev: 2-2, Up: Chapter 2
Free Your Mind: Interactive Programming
=======================================
When you start Lisp in a Box, you should see a buffer containing a
prompt that looks like this:
CL-USER>
This is the Lisp prompt. Like a Unix or DOS shell prompt, the Lisp
prompt is a place where you can type expressions that will cause things
to happen. However, instead of reading and interpreting a line of shell
commands, Lisp reads Lisp expressions, evaluates them according to the
rules of Lisp, and prints the result. Then it does it again with the
next expression you type. That endless cycle of reading, evaluating,
and printing is why it's called the ‘read-eval-print loop’, or REPL
for short. It's also referred to as the ‘top-level’, the
‘top-level listener’, or the ‘Lisp listener’.
From within the environment provided by the REPL, you can define and
redefine program elements such as variables, functions, classes, and
methods; evaluate any Lisp expression; load files containing Lisp source
code or compiled code; compile whole files or individual functions;
enter the debugger; step through code; and inspect the state of
individual Lisp objects.
All those facilities are built into the language, accessible via
functions defined in the language standard. If you had to, you could
build a pretty reasonable programming environment out of just the REPL
and any text editor that knows how to properly indent Lisp code. But
for the true Lisp programming experience, you need an environment, such
as SLIME, that lets you interact with Lisp both via the REPL and while
editing source files. For instance, you don't want to have to cut and
paste a function definition from a source file to the REPL or have to
load a whole file just because you changed one function; your Lisp
environment should let us evaluate or compile both individual
expressions and whole files directly from your editor.
File: pcl.info, Node: 2-4, Next: 2-5, Prev: 2-3, Up: Chapter 2
Experimenting in the REPL
=========================
To try the REPL, you need a Lisp expression that can be read, evaluated,
and printed. One of the simplest kinds of Lisp expressions is a number.
At the Lisp prompt, you can type 10 followed by Return and should see
something like this:
CL-USER> 10
10
The first 10 is the one you typed. The Lisp reader, the R in REPL,
reads the text "10" and creates a Lisp object representing the number
10. This object is a ‘self-evaluating object’, which means that
when given to the evaluator, the E in REPL, it evaluates to itself.
This value is then given to the printer, which prints the 10 on the line
by itself. While that may seem like a lot of work just to get back to
where you started, things get a bit more interesting when you give Lisp
something meatier to chew on. For instance, you can type (+ 2 3) at the
Lisp prompt.
CL-USER> (+ 2 3)
5
Anything in parentheses is a list, in this case a list of three
elements, the symbol +, and the numbers 2 and 3. Lisp, in general,
evaluates lists by treating the first element as the name of a function
and the rest of the elements as expressions to be evaluated to yield the
arguments to the function. In this case, the symbol + names a function
that performs addition. 2 and 3 evaluate to themselves and are then
passed to the addition function, which returns 5. The value 5 is passed
to the printer, which prints it. Lisp can evaluate a list expression in
other ways, but we needn't get into them right away. First we have to
write. . .
File: pcl.info, Node: 2-5, Next: 2-6, Prev: 2-4, Up: Chapter 2
"Hello, World," Lisp Style
==========================
No programming book is complete without a "hello, world" (1) program.
As it turns out, it's trivially easy to get the REPL to print "hello,
world."
CL-USER> "hello, world"
"hello, world"
This works because strings, like numbers, have a literal syntax that's
understood by the Lisp reader and are self-evaluating objects: Lisp
reads the double-quoted string and instantiates a string object in
memory that, when evaluated, evaluates to itself and is then printed in
the same literal syntax. The quotation marks aren't part of the string
object in memory-they're just the syntax that tells the reader to read a
string. The printer puts them back on when it prints the string because
it tries to print objects in the same syntax the reader understands.
However, this may not really qualify as a "hello, world" ‘program’.
It's more like the "hello, world" ‘value’.
You can take a step toward a real program by writing some code that as a
side effect prints the string "hello, world" to standard output. Common
Lisp provides a couple ways to emit output, but the most flexible is the
‘FORMAT’ function. ‘FORMAT’ takes a variable number of
arguments, but the only two required arguments are the place to send the
output and a string. You'll see in the next chapter how the string can
contain embedded directives that allow you to interpolate subsequent
arguments into the string, à la ‘printf’ or Python's string-%. As
long as the string doesn't contain an ~, it will be emitted as-is. If
you pass t as its first argument, it sends its output to standard
output. So a ‘FORMAT’ expression that will print "hello, world"
looks like this (2) :
CL-USER> (format t "hello, world")
hello, world
NIL
One thing to note about the result of the ‘FORMAT’ expression is the
‘NIL’ on the line after the "hello, world" output. That ‘NIL’
is the result of evaluating the ‘FORMAT’ expression, printed by the
REPL. (‘NIL’ is Lisp's version of false and/or null. More on that
in Chapter 4.) Unlike the other expressions we've seen so far, a
‘FORMAT’ expression is more interesting for its side effect-printing
to standard output in this case-than for its return value. But every
expression in Lisp evaluates to some result (3) .
However, it's still arguable whether you've yet written a true
"program." But you're getting there. And you're seeing the bottom-up
style of programming supported by the REPL: you can experiment with
different approaches and build a solution from parts you've already
tested. Now that you have a simple expression that does what you want,
you just need to package it in a function. Functions are one of the
basic program building blocks in Lisp and can be defined with a
‘DEFUN’ expression such as this:
CL-USER> (defun hello-world () (format t "hello, world"))
HELLO-WORLD
The ‘hello-world’ after the ‘DEFUN’ is the name of the function.
In Chapter 4 we'll look at exactly what characters can be used in a
name, but for now suffice it to say that lots of characters, such as -,
that are illegal in names in other languages are legal in Common Lisp.
It's standard Lisp style-not to mention more in line with normal English
typography-to form compound names with hyphens, such as
‘hello-world’, rather than with underscores, as in
‘hello_world’, or with inner caps such as ‘helloWorld’. The ()s
after the name delimit the parameter list, which is empty in this case
because the function takes no arguments. The rest is the body of the
function.
At one level, this expression, like all the others you've seen, is just
another expression to be read, evaluated, and printed by the REPL. The
return value in this case is the name of the function you just defined
(4) . But like the ‘FORMAT’ expression, this expression is more
interesting for the side effects it has than for its return value.
Unlike the ‘FORMAT’ expression, however, the side effects are
invisible: when this expression is evaluated, a new function that takes
no arguments and with the body (format t "hello, world") is created and
given the name HELLO-WORLD.
Once you've defined the function, you can call it like this:
CL-USER> (hello-world)
hello, world
NIL
You can see that the output is just the same as when you evaluated the
‘FORMAT’ expression directly, including the ‘NIL’ value printed
by the REPL. Functions in Common Lisp automatically return the value of
the last expression evaluated.
---------- Footnotes ----------
(1) The venerable "hello, world" predates even the classic Kernighan
and Ritchie C book that played a big role in its popularization. The
original "hello, world" seems to have come from Brian Kernighan's "A
Tutorial Introduction to the Language B" that was part of the ‘Bell
Laboratories Computing Science Technical Report #8: The Programming
Language B’ published in January 1973. (It's available online at
http://cm.bell-labs.com/cm/cs/who/dmr/bintro.html.)
(2) These are some other expressions that also print the string
"hello, world":
(3) Well, as you'll see when I discuss returning multiple values,
it's technically possible to write expressions that evaluate to no
value, but even such expressions are treated as returning NIL when
evaluated in a context that expects a value.
(4) I'll discuss in Chapter 4 why the name has been converted to all
uppercase.
File: pcl.info, Node: 2-6, Next: Chapter 3, Prev: 2-5, Up: Chapter 2
Saving Your Work
================
You could argue that this is a complete "hello, world" program of sorts.
However, it still has a problem. If you exit Lisp and restart, the
function definition will be gone. Having written such a fine function,
you'll want to save your work.
Easy enough. You just need to create a file in which to save the
definition. In Emacs you can create a new file by typing C-x C-f and
then, when Emacs prompts you, entering the name of the file you want to
create. It doesn't matter particularly where you put the file. It's
customary to name Common Lisp source files with a .lisp extension,
though some folks use .cl instead.
Once you've created the file, you can type the definition you previously
entered at the REPL. Some things to note are that after you type the
opening parenthesis and the word ‘DEFUN’, at the bottom of the Emacs
window, SLIME will tell you the arguments expected. The exact form will
depend somewhat on what Common Lisp implementation you're using, but
it'll probably look something like this:
(defun name varlist &rest body)
The message will disappear as you start to type each new element but
will reappear each time you enter a space. When you're entering the
definition in the file, you might choose to break the definition across
two lines after the parameter list. If you hit Return and then Tab,
SLIME will automatically indent the second line appropriately, like this
(1) :
(defun hello-world ()
(format t "hello, world"))
SLIME will also help match up the parentheses-as you type a closing
parenthesis, it will flash the corresponding opening parenthesis. Or
you can just type C-c C-q to invoke the command
‘slime-close-parens-at-point’, which will insert as many closing
parentheses as necessary to match all the currently open parentheses.
Now you can get this definition into your Lisp environment in several
ways. The easiest is to type C-c C-c with the cursor anywhere in or
immediately after the ‘DEFUN’ form, which runs the command
‘slime-compile-defun’, which in turn sends the definition to Lisp to
be evaluated and compiled. To make sure this is working, you can make
some change to hello-world, recompile it, and then go back to the REPL,
using C-c C-z or C-x b, and call it again. For instance, you could make
it a bit more grammatical.
(defun hello-world ()
(format t "Hello, world!"))
Next, recompile with C-c C-c and then type C-c C-z to switch to the REPL
to try the new version.
CL-USER> (hello-world)
Hello, world!
NIL
You'll also probably want to save the file you've been working on; in
the hello.lisp buffer, type C-x C-s to invoke the Emacs command
‘save-buffer’.
Now to try reloading this function from the source file, you'll need to
quit Lisp and restart. To quit you can use a SLIME shortcut: at the
REPL, type a comma. At the bottom of the Emacs window, you will be
prompted for a command. Type ‘quit’ (or ‘sayoonara’), and then
hit Enter. This will quit Lisp and close all the buffers created by
SLIME such as the REPL buffer (2) . Now restart SLIME by typing ‘M-x
slime’.
Just for grins, you can try to invoke ‘hello-world’.
CL-USER> (hello-world)
At that point SLIME will pop up a new buffer that starts with something
that looks like this:
attempt to call `HELLO-WORLD' which is an undefined function.
[Condition of type UNDEFINED-FUNCTION]
Restarts:
0: [TRY-AGAIN] Try calling HELLO-WORLD again.
1: [RETURN-VALUE] Return a value instead of calling HELLO-WORLD.
2: [USE-VALUE] Try calling a function other than HELLO-WORLD.
3: [STORE-VALUE] Setf the symbol-function of HELLO-WORLD and call it again.
4: [ABORT] Abort handling SLIME request.
5: [ABORT] Abort entirely from this process.
Backtrace:
0: (SWANK::DEBUG-IN-EMACS #<UNDEFINED-FUNCTION #x716b082a>)
1: ((FLET SWANK:SWANK-DEBUGGER-HOOK SWANK::DEBUG-IT))
2: (SWANK:SWANK-DEBUGGER-HOOK #<UNDEFINED-FUNCTION #x716b082a> #<Function SWANK-DEBUGGER-HOOK>)
3: (ERROR #<UNDEFINED-FUNCTION #x716b082a>)
4: (EVAL (HELLO-WORLD))
5: (SWANK::EVAL-REGION "(hello-world)
" T)
Blammo! What happened? Well, you tried to invoke a function that
doesn't exist. But despite the burst of output, Lisp is actually
handling this situation gracefully. Unlike Java or Python, Common Lisp
doesn't just bail-throwing an exception and unwinding the stack. And it
definitely doesn't dump core just because you tried to invoke a missing
function. Instead Lisp drops you into the debugger.
While you're in the debugger you still have full access to Lisp, so you
can evaluate expressions to examine the state of our program and maybe
even fix things. For now don't worry about that; just type q to exit
the debugger and get back to the REPL. The debugger buffer will go away,
and the REPL will show this:
CL-USER> (hello-world)
; Evaluation aborted
CL-USER>
There's obviously more that can be done from within the debugger than
just abort-we'll see, for instance, in Chapter 19 how the debugger
integrates with the error handling system. For now, however, the
important thing to know is that you can always get out of it, and back
to the REPL, by typing ‘q’.
Back at the REPL you can try again. Things blew up because Lisp didn't
know the definition of hello-world. So you need to let Lisp know about
the definition we saved in the file hello.lisp. You have several ways
you could do this. You could switch back to the buffer containing the
file (type C-x b and then enter hello.lisp when prompted) and recompile
the definition as you did before with C-c C-c. Or you can load the
whole file, which would be a more convenient approach if the file
contained a bunch of definitions, using the LOAD function at the REPL
like this:
CL-USER> (load "hello.lisp")
; Loading /home/peter/my-lisp-programs/hello.lisp
T
The ‘T’ means everything loaded correctly (3) . Loading a file with
LOAD is essentially equivalent to typing each of the expressions in the
file at the REPL in the order they appear in the file, so after the call
to ‘LOAD’, hello-world should be defined:
CL-USER> (hello-world)
Hello, world!
NIL
Another way to load a file's worth of definitions is to compile the file
first with ‘COMPILE-FILE’ and then ‘LOAD’ the resulting compiled
file, called a ‘FASL file’, which is short for ‘fast-load file’.
‘COMPILE-FILE’ returns the name of the FASL file, so we can compile
and load from the REPL like this:
CL-USER> (load (compile-file "hello.lisp"))
;;; Compiling file hello.lisp
;;; Writing fasl file hello.fasl
;;; Fasl write complete
v; Fast loading /home/peter/my-lisp-programs/hello.fasl
T
SLIME also provides support for loading and compiling files without
using the REPL. When you're in a source code buffer, you can use C-c C-l
to load the file with ‘slime-load-file’. Emacs will prompt for the
name of a file to load with the name of the current file already filled
in; you can just hit Enter. Or you can type C-c C-k to compile and load
the file represented by the current buffer. In some Common Lisp
implementations, compiling code this way will make it quite a bit
faster; in others, it won't, typically because they always compile
everything.
This should be enough to give you a flavor of how Lisp programming
works. Of course I haven't covered all the tricks and techniques yet,
but you've seen the essential elements-interacting with the REPL trying
things out, loading and testing new code, tweaking and debugging.
Serious Lisp hackers often keep a Lisp image running for days on end,
adding, redefining, and testing bits of their program incrementally.
Also, even when the Lisp app is deployed, there's often still a way to
get to a REPL. You'll see in Chapter 26 how you can use the REPL and
SLIME to interact with the Lisp that's running a Web server at the same
time as it's serving up Web pages. It's even possible to use SLIME to
connect to a Lisp running on a different machine, allowing you-for
instance-to debug a remote server just like a local one.
An even more impressive instance of remote debugging occurred on NASA's
1998 Deep Space 1 mission. A half year after the space craft launched,
a bit of Lisp code was going to control the spacecraft for two days
while conducting a sequence of experiments. Unfortunately, a subtle
race condition in the code had escaped detection during ground testing
and was already in space. When the bug manifested in the wild-100
million miles away from Earth-the team was able to diagnose and fix the
running code, allowing the experiments to complete (4) . One of the
programmers described it as follows:
Debugging a program running on a $100M piece of hardware that is
100 million miles away is an interesting experience. Having a
read-eval-print loop running on the spacecraft proved invaluable in
finding and fixing the problem.
You're not quite ready to send any Lisp code into deep space, but in the
next chapter you'll take a crack at writing a program a bit more
interesting than "hello, world."
---------- Footnotes ----------
(1) You could also have entered the definition as two lines at the
REPL, as the REPL reads whole expressions, not lines.
(2) SLIME shortcuts aren't part of Common Lisp-they're commands to
SLIME.
(3) If for some reason the ‘LOAD’ doesn't go cleanly, you'll get
another error and drop back into the debugger. If this happens, the
most likely reason is that Lisp can't find the file, probably because
its idea of the current working directory isn't the same as where the
file is located. In that case, you can quit the debugger by typing q
and then use the SLIME shortcut cd to change Lisp's idea of the current
directory-type a comma and then cd when prompted for a command and then
the name of the directory where hello.lisp was saved.
(4) http://www.flownet.com/gat/jpl-lisp.html
File: pcl.info, Node: Chapter 3, Next: Chapter 4, Prev: Chapter 2, Up: Top
3. Practical: A Simple Database
===============================
Obviously, before you can start building real software in Lisp, you'll
have to learn the language. But let's face it-you may be thinking,
"'Practical Common Lisp,' isn't that an oxymoron? Why should you be
expected to bother learning all the details of a language unless it's
actually good for something you care about?" So I'll start by giving
you a small example of what you can do with Common Lisp. In this
chapter you'll write a simple database for keeping track of CDs. You'll
use similar techniques in Chapter 27 when you build a database of MP3s
for our streaming MP3 server. In fact, you could think of this as part
of the MP3 software project-after all, in order to have a bunch of MP3s
to listen to, it might be helpful to be able to keep track of which CDs
you have and which ones you need to rip.
In this chapter, I'll cover just enough Lisp as we go along for you
to understand how the code works. But I'll gloss over quite a few
details. For now you needn't sweat the small stuff-the next several
chapters will cover all the Common Lisp constructs used here, and more,
in a much more systematic way.
One terminology note: I'll discuss a handful of Lisp operators in
this chapter. In Chapter 4, you'll learn that Common Lisp provides
three distinct kinds of operators: functions, macros, and special
operators. For the purposes of this chapter, you don't really need to
know the difference. I will, however, refer to different operators as
functions or macros or special operators as appropriate, rather than
trying to hide the details behind the word operator. For now you can
treat function, macro, and special operator as all more or less
equivalent. (1)
Also, keep in mind that I won't bust out all the most sophisticated
Common Lisp techniques for your very first post-"hello, world" program.
The point of this chapter isn't that this is how you would write a
database in Lisp; rather, the point is for you to get an idea of what
programming in Lisp is like and to see how even a relatively simple Lisp
program can be quite featureful.
* Menu:
* 3-1:: CDs and Records
* 3-2:: Filing CDs
* 3-3:: Looking at the Database Contents
* 3-4:: Improving the User Interaction
* 3-5:: Saving and Loading the Database
* 3-6:: Querying the Database
* 3-7:: Updating Existing Records-Another Use for WHERE
* 3-8:: Removing Duplication and Winning Big
* 3-9:: Wrapping Up
---------- Footnotes ----------
(1) Before I proceed, however, it's crucially important that you
forget anything you may know about #define-style "macros" as implemented
in the C pre-processor. Lisp macros are a totally different beast.
File: pcl.info, Node: 3-1, Next: 3-2, Prev: Chapter 3, Up: Chapter 3
CDs and Records
===============
To keep track of CDs that need to be ripped to MP3s and which CDs should
be ripped first, each record in the database will contain the title and
artist of the CD, a rating of how much the user likes it, and a flag
saying whether it has been ripped. So, to start with, you'll need a way
to represent a single database record (in other words, one CD). Common
Lisp gives you lots of choices of data structures from a simple
four-item list to a user-defined class, using the Common Lisp Object
System (CLOS).
For now you can stay at the simple end of the spectrum and use a
list. You can make a list with the LIST function, which, appropriately
enough, returns a list of its arguments.
CL-USER> (list 1 2 3)
(1 2 3)
You could use a four-item list, mapping a given position in the list
to a given field in the record. However, another flavor of list-called
a property list, or plist for short-is even more convenient. A plist is
a list where every other element, starting with the first, is a symbol
that describes what the next element in the list is. I won't get into
all the details of exactly what a symbol is right now; basically it's a
name. For the symbols that name the fields in the CD database, you can
use a particular kind of symbol, called a keyword symbol. A keyword is
any name that starts with a colon (:), for instance, :foo. Here's an
example of a plist using the keyword symbols :a, :b, and :c as property
names:
CL-USER> (list :a 1 :b 2 :c 3)
(:A 1 :B 2 :C 3)
Note that you can create a property list with the same LIST function
as you use to create other lists; it's the contents that make it a
plist.
The thing that makes plists a convenient way to represent the records
in a database is the function GETF, which takes a plist and a symbol and
returns the value in the plist following the symbol, making a plist a
sort of poor man's hash table. Lisp has real hash tables too, but
plists are sufficient for your needs here and can more easily be saved
to a file, which will come in handy later.
CL-USER> (getf (list :a 1 :b 2 :c 3) :a)
1
CL-USER> (getf (list :a 1 :b 2 :c 3) :c)
3
Given all that, you can easily enough write a function make-cd that
will take the four fields as arguments and return a plist representing
that CD.
(defun make-cd (title artist rating ripped)
(list :title title :artist artist :rating rating :ripped ripped))
The word DEFUN tells us that this form is defining a new function.
The name of the function is make-cd. After the name comes the parameter
list. This function has four parameters: title, artist, rating, and
ripped. Everything after the parameter list is the body of the
function. In this case the body is just one form, a call to LIST. When
make-cd is called, the arguments passed to the call will be bound to the
variables in the parameter list. For instance, to make a record for the
CD Roses by Kathy Mattea, you might call make-cd like this:
CL-USER> (make-cd "Roses" "Kathy Mattea" 7 t)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T)
File: pcl.info, Node: 3-2, Next: 3-3, Prev: 3-1, Up: Chapter 3
Filing CDs
==========
A single record, however, does not a database make. You need some
larger construct to hold the records. Again, for simplicity's sake, a
list seems like a good choice. Also for simplicity you can use a global
variable, *db*, which you can define with the DEFVAR macro. The
asterisks (*) in the name are a Lisp naming convention for global
variables. (1)
(defvar *db* nil)
You can use the PUSH macro to add items to *db*. But it's probably a
good idea to abstract things a tiny bit, so you should define a function
add-record that adds a record to the database.
(defun add-record (cd) (push cd *db*))
Now you can use add-record and make-cd together to add CDs to the
database.
CL-USER> (add-record (make-cd "Roses" "Kathy Mattea" 7 t))
((:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
CL-USER> (add-record (make-cd "Fly" "Dixie Chicks" 8 t))
((:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
CL-USER> (add-record (make-cd "Home" "Dixie Chicks" 9 t))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
The stuff printed by the REPL after each call to add-record is the
return value, which is the value returned by the last expression in the
function body, the PUSH. And PUSH returns the new value of the variable
it's modifying. So what you're actually seeing is the value of the
database after the record has been added.
---------- Footnotes ----------
(1) Using a global variable also has some drawbacks-for instance, you
can have only one database at a time. In Chapter 27, with more of the
language under your belt, you'll be ready to build a more flexible
database. You'll also see, in Chapter 6, how even using a global
variable is more flexible in Common Lisp than it may be in other
languages.
File: pcl.info, Node: 3-3, Next: 3-4, Prev: 3-2, Up: Chapter 3
Looking at the Database Contents
================================
You can also see the current value of *db* whenever you want by typing
*db* at the REPL.
CL-USER> *db*
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
However, that's not a very satisfying way of looking at the output.
You can write a dump-db function that dumps out the database in a more
human-readable format, like this:
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T
TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T
TITLE: Roses
ARTIST: Kathy Mattea
RATING: 7
RIPPED: T
The function looks like this:
(defun dump-db ()
(dolist (cd *db*)
(format t "~{~a:~10t~a~%~}~%" cd)))
This function works by looping over all the elements of *db* with the
DOLIST macro, binding each element to the variable cd in turn. For each
value of cd, you use the FORMAT function to print it.
Admittedly, the FORMAT call is a little cryptic. However, FORMAT
isn't particularly more complicated than C or Perl's printf function or
Python's string-% operator. In Chapter 18 I'll discuss FORMAT in
greater detail. For now we can take this call bit by bit. As you saw
in Chapter 2, FORMAT takes at least two arguments, the first being the
stream where it sends its output; t is shorthand for the stream
*standard-output*.
The second argument to FORMAT is a format string that can contain
both literal text and directives telling FORMAT things such as how to
interpolate the rest of its arguments. Format directives start with ~
(much the way printf's directives start with %). FORMAT understands
dozens of directives, each with their own set of options. (1) However,
for now I'll just focus on the ones you need to write dump-db.
The ~a directive is the aesthetic directive; it means to consume one
argument and output it in a human-readable form. This will render
keywords without the leading : and strings without quotation marks. For
instance:
CL-USER> (format t "~a" "Dixie Chicks")
Dixie Chicks
NIL
or:
CL-USER> (format t "~a" :title)
TITLE
NIL
The ~t directive is for tabulating. The ~10t tells FORMAT to emit
enough spaces to move to the tenth column before processing the next ~a.
A ~t doesn't consume any arguments.
CL-USER> (format t "~a:~10t~a" :artist "Dixie Chicks")
ARTIST: Dixie Chicks
NIL
Now things get slightly more complicated. When FORMAT sees ~{ the
next argument to be consumed must be a list. FORMAT loops over that
list, processing the directives between the ~{ and ~}, consuming as many
elements of the list as needed each time through the list. In dump-db,
the FORMAT loop will consume one keyword and one value from the list
each time through the loop. The ~% directive doesn't consume any
arguments but tells FORMAT to emit a newline. Then after the ~} ends
the loop, the last ~% tells FORMAT to emit one more newline to put a
blank line between each CD.
Technically, you could have also used FORMAT to loop over the
database itself, turning our dump-db function into a one-liner.
(defun dump-db ()
(format t "~{~{~a:~10t~a~%~}~%~}" *db*))
That's either very cool or very scary depending on your point of
view.
---------- Footnotes ----------
(1) One of the coolest FORMAT directives is the ~R directive. Ever
want to know how to say a really big number in English words? Lisp
knows. Evaluate this:
(format nil "~r" 1606938044258990275541962092)
and you should get back (wrapped for legibility):
File: pcl.info, Node: 3-4, Next: 3-5, Prev: 3-3, Up: Chapter 3
Improving the User Interaction
==============================
While our add-record function works fine for adding records, it's a bit
Lispy for the casual user. And if they want to add a bunch of records,
it's not very convenient. So you may want to write a function to prompt
the user for information about a set of CDs. Right away you know you'll
need some way to prompt the user for a piece of information and read it.
So let's write that.
(defun prompt-read (prompt)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
You use your old friend FORMAT to emit a prompt. Note that there's
no ~% in the format string, so the cursor will stay on the same line.
The call to FORCE-OUTPUT is necessary in some implementations to ensure
that Lisp doesn't wait for a newline before it prints the prompt.
Then you can read a single line of text with the aptly named
READ-LINE function. The variable *query-io* is a global variable (which
you can tell because of the * naming convention for global variables)
that contains the input stream connected to the terminal. The return
value of prompt-read will be the value of the last form, the call to
READ-LINE, which returns the string it read (without the trailing
newline.)
You can combine your existing make-cd function with prompt-read to
build a function that makes a new CD record from data it gets by
prompting for each value in turn.
(defun prompt-for-cd ()
(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(prompt-read "Rating")
(prompt-read "Ripped [y/n]")))
That's almost right. Except prompt-read returns a string, which,
while fine for the Title and Artist fields, isn't so great for the
Rating and Ripped fields, which should be a number and a boolean.
Depending on how sophisticated a user interface you want, you can go to
arbitrary lengths to validate the data the user enters. For now let's
lean toward the quick and dirty: you can wrap the prompt-read for the
rating in a call to Lisp's PARSE-INTEGER function, like this:
(parse-integer (prompt-read "Rating"))
Unfortunately, the default behavior of PARSE-INTEGER is to signal an
error if it can't parse an integer out of the string or if there's any
non-numeric junk in the string. However, it takes an optional keyword
argument :junk-allowed, which tells it to relax a bit.
(parse-integer (prompt-read "Rating") :junk-allowed t)
But there's still one problem: if it can't find an integer amidst all
the junk, PARSE-INTEGER will return NIL rather than a number. In
keeping with the quick-and-dirty approach, you may just want to call
that 0 and continue. Lisp's OR macro is just the thing you need here.
It's similar to the "short-circuiting" || in Perl, Python, Java, and C;
it takes a series of expressions, evaluates them one at a time, and
returns the first non-nil value (or NIL if they're all NIL). So you can
use the following:
(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
to get a default value of 0.
Fixing the code to prompt for Ripped is quite a bit simpler. You can
just use the Common Lisp function Y-OR-N-P.
(y-or-n-p "Ripped [y/n]: ")
In fact, this will be the most robust part of prompt-for-cd, as
Y-OR-N-P will reprompt the user if they enter something that doesn't
start with y, Y, n, or N.
Putting those pieces together you get a reasonably robust
prompt-for-cd function.
(defun prompt-for-cd ()
(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
(y-or-n-p "Ripped [y/n]: ")))
Finally, you can finish the "add a bunch of CDs" interface by
wrapping prompt-for-cd in a function that loops until the user is done.
You can use the simple form of the LOOP macro, which repeatedly executes
a body of expressions until it's exited by a call to RETURN. For
example:
(defun add-cds ()
(loop (add-record (prompt-for-cd))
(if (not (y-or-n-p "Another? [y/n]: ")) (return))))
Now you can use add-cds to add some more CDs to the database.
CL-USER> (add-cds)
Title: Rockin' the Suburbs
Artist: Ben Folds
Rating: 6
Ripped [y/n]: y
Another? [y/n]: y
Title: Give Us a Break
Artist: Limpopo
Rating: 10
Ripped [y/n]: y
Another? [y/n]: y
Title: Lyle Lovett
Artist: Lyle Lovett
Rating: 9
Ripped [y/n]: y
Another? [y/n]: n
NIL
File: pcl.info, Node: 3-5, Next: 3-6, Prev: 3-4, Up: Chapter 3
Saving and Loading the Database
===============================
Having a convenient way to add records to the database is nice. But
it's not so nice that the user is going to be very happy if they have to
reenter all the records every time they quit and restart Lisp. Luckily,
with the data structures you're using to represent the data, it's
trivially easy to save the data to a file and reload it later. Here's a
save-db function that takes a filename as an argument and saves the
current state of the database:
(defun save-db (filename)
(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
The WITH-OPEN-FILE macro opens a file, binds the stream to a
variable, executes a set of expressions, and then closes the file. It
also makes sure the file is closed even if something goes wrong while
evaluating the body. The list directly after WITH-OPEN-FILE isn't a
function call but rather part of the syntax defined by WITH-OPEN-FILE.
It contains the name of the variable that will hold the file stream to
which you'll write within the body of WITH-OPEN-FILE, a value that must
be a file name, and then some options that control how the file is
opened. Here you specify that you're opening the file for writing with
:direction :output and that you want to overwrite an existing file of
the same name if it exists with :if-exists :supersede.
Once you have the file open, all you have to do is print the contents
of the database with (print *db* out). Unlike FORMAT, PRINT prints Lisp
objects in a form that can be read back in by the Lisp reader. The
macro WITH-STANDARD-IO-SYNTAX ensures that certain variables that affect
the behavior of PRINT are set to their standard values. You'll use the
same macro when you read the data back in to make sure the Lisp reader
and printer are operating compatibly.
The argument to save-db should be a string containing the name of the
file where the user wants to save the database. The exact form of the
string will depend on what operating system they're using. For
instance, on a Unix box they should be able to call save-db like this:
CL-USER> (save-db "~/my-cds.db")
((:TITLE "Lyle Lovett" :ARTIST "Lyle Lovett" :RATING 9 :RIPPED T)
(:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T)
(:TITLE "Rockin' the Suburbs" :ARTIST "Ben Folds" :RATING 6 :RIPPED
T)
(:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 9 :RIPPED T))
On Windows, the filename might be something like "c:/my-cds.db" or
"c:\\my-cds.db." (1)
You can open this file in any text editor to see what it looks like.
You should see something a lot like what the REPL prints if you type
*db*.
The function to load the database back in is similar.
(defun load-db (filename)
(with-open-file (in filename)
(with-standard-io-syntax
(setf *db* (read in)))))
This time you don't need to specify :direction in the options to
WITH-OPEN-FILE, since you want the default of :input. And instead of
printing, you use the function READ to read from the stream in. This is
the same reader used by the REPL and can read any Lisp expression you
could type at the REPL prompt. However, in this case, you're just
reading and saving the expression, not evaluating it. Again, the
WITH-STANDARD-IO-SYNTAX macro ensures that READ is using the same basic
syntax that save-db did when it PRINTed the data.
The SETF macro is Common Lisp's main assignment operator. It sets
its first argument to the result of evaluating its second argument. So
in load-db the *db* variable will contain the object read from the file,
namely, the list of lists written by save-db. You do need to be careful
about one thing-load-db clobbers whatever was in *db* before the call.
So if you've added records with add-record or add-cds that haven't been
saved with save-db, you'll lose them.
---------- Footnotes ----------
(1) Windows actually understands forward slashes in filenames even
though it normally uses a backslash as the directory separator. This is
convenient since otherwise you have to write double backslashes because
backslash is the escape character in Lisp strings.
File: pcl.info, Node: 3-6, Next: 3-7, Prev: 3-5, Up: Chapter 3
Querying the Database
=====================
Now that you have a way to save and reload the database to go along with
a convenient user interface for adding new records, you soon may have
enough records that you won't want to be dumping out the whole database
just to look at what's in it. What you need is a way to query the
database. You might like, for instance, to be able to write something
like this:
(select :artist "Dixie Chicks")
and get a list of all the records where the artist is the Dixie
Chicks. Again, it turns out that the choice of saving the records in a
list will pay off.
The function REMOVE-IF-NOT takes a predicate and a list and returns a
list containing only the elements of the original list that match the
predicate. In other words, it has removed all the elements that don't
match the predicate. However, REMOVE-IF-NOT doesn't really remove
anything-it creates a new list, leaving the original list untouched.
It's like running grep over a file. The predicate argument can be any
function that accepts a single argument and returns a boolean value-NIL
for false and anything else for true.
For instance, if you wanted to extract all the even elements from a
list of numbers, you could use REMOVE-IF-NOT as follows:
CL-USER> (remove-if-not #'evenp '(1 2 3 4 5 6 7 8 9 10))
(2 4 6 8 10)
In this case, the predicate is the function EVENP, which returns true
if its argument is an even number. The funny notation #' is shorthand
for "Get me the function with the following name." Without the #', Lisp
would treat evenp as the name of a variable and look up the value of the
variable, not the function.
You can also pass REMOVE-IF-NOT an anonymous function. For instance,
if EVENP didn't exist, you could write the previous expression as the
following:
CL-USER> (remove-if-not #'(lambda (x) (= 0 (mod x 2))) '(1 2 3 4 5 6 7 8 9 10))
(2 4 6 8 10)
In this case, the predicate is this anonymous function:
(lambda (x) (= 0 (mod x 2)))
which checks that its argument is equal to 0 modulus 2 (in other
words, is even). If you wanted to extract only the odd numbers using an
anonymous function, you'd write this:
CL-USER> (remove-if-not #'(lambda (x) (= 1 (mod x 2))) '(1 2 3 4 5 6 7 8 9 10))
(1 3 5 7 9)
Note that lambda isn't the name of the function-it's the indicator
you're defining an anonymous function. (1) Other than the lack of a
name, however, a LAMBDA expression looks a lot like a DEFUN: the word
lambda is followed by a parameter list, which is followed by the body of
the function.
To select all the Dixie Chicks' albums in the database using
REMOVE-IF-NOT, you need a function that returns true when the artist
field of a record is "Dixie Chicks". Remember that we chose the plist
representation for the database records because the function GETF can
extract named fields from a plist. So assuming cd is the name of a
variable holding a single database record, you can use the expression
(getf cd :artist) to extract the name of the artist. The function
EQUAL, when given string arguments, compares them character by
character. So (equal (getf cd :artist) "Dixie Chicks") will test
whether the artist field of a given CD is equal to "Dixie Chicks". All
you need to do is wrap that expression in a LAMBDA form to make an
anonymous function and pass it to REMOVE-IF-NOT.
CL-USER> (remove-if-not
#'(lambda (cd) (equal (getf cd :artist) "Dixie Chicks")) *db*)
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T))
Now suppose you want to wrap that whole expression in a function that
takes the name of the artist as an argument. You can write that like
this:
(defun select-by-artist (artist)
(remove-if-not
#'(lambda (cd) (equal (getf cd :artist) artist))
*db*))
Note how the anonymous function, which contains code that won't run
until it's invoked in REMOVE-IF-NOT, can nonetheless refer to the
variable artist. In this case the anonymous function doesn't just save
you from having to write a regular function-it lets you write a function
that derives part of its meaning-the value of artist-from the context in
which it's embedded.
So that's select-by-artist. However, selecting by artist is only one
of the kinds of queries you might like to support. You could write
several more functions, such as select-by-title, select-by-rating,
select-by-title-and-artist, and so on. But they'd all be about the same
except for the contents of the anonymous function. You can instead make
a more general select function that takes a function as an argument.
(defun select (selector-fn)
(remove-if-not selector-fn *db*))
So what happened to the #'? Well, in this case you don't want
REMOVE-IF-NOT to use the function named selector-fn. You want it to use
the anonymous function that was passed as an argument to select in the
variable selector-fn. Though, the #' comes back in the call to select.
CL-USER> (select #'(lambda (cd) (equal (getf cd :artist) "Dixie Chicks")))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T))
But that's really quite gross-looking. Luckily, you can wrap up the
creation of the anonymous function.
(defun artist-selector (artist)
#'(lambda (cd) (equal (getf cd :artist) artist)))
This is a function that returns a function and one that references a
variable that-it seems-won't exist after artist-selector returns. (2)
It may seem odd now, but it actually works just the way you'd want-if
you call artist-selector with an argument of "Dixie Chicks", you get an
anonymous function that matches CDs whose :artist field is "Dixie
Chicks", and if you call it with "Lyle Lovett", you get a different
function that will match against an :artist field of "Lyle Lovett". So
now you can rewrite the call to select like this:
CL-USER> (select (artist-selector "Dixie Chicks"))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T))
Now you just need some more functions to generate selectors. But
just as you don't want to have to write select-by-title,
select-by-rating, and so on, because they would all be quite similar,
you're not going to want to write a bunch of nearly identical
selector-function generators, one for each field. Why not write one
general-purpose selector-function generator, a function that, depending
on what arguments you pass it, will generate a selector function for
different fields or maybe even a combination of fields? You can write
such a function, but first you need a crash course in a feature called
keyword parameters.
In the functions you've written so far, you've specified a simple
list of parameters, which are bound to the corresponding arguments in
the call to the function. For instance, the following function:
(defun foo (a b c) (list a b c))
has three parameters, a, b, and c, and must be called with three
arguments. But sometimes you may want to write a function that can be
called with varying numbers of arguments. Keyword parameters are one
way to achieve this. A version of foo that uses keyword parameters
might look like this:
(defun foo (&key a b c) (list a b c))
The only difference is the &key at the beginning of the argument
list. However, the calls to this new foo will look quite different.
These are all legal calls with the result to the right of the ==>:
(foo :a 1 :b 2 :c 3) ==> (1 2 3)
(foo :c 3 :b 2 :a 1) ==> (1 2 3)
(foo :a 1 :c 3) ==> (1 NIL 3)
(foo) ==> (NIL NIL NIL)
As these examples show, the value of the variables a, b, and c are
bound to the values that follow the corresponding keyword. And if a
particular keyword isn't present in the call, the corresponding variable
is set to NIL. I'm glossing over a bunch of details of how keyword
parameters are specified and how they relate to other kinds of
parameters, but you need to know one more detail.
Normally if a function is called with no argument for a particular
keyword parameter, the parameter will have the value NIL. However,
sometimes you'll want to be able to distinguish between a NIL that was
explicitly passed as the argument to a keyword parameter and the default
value NIL. To allow this, when you specify a keyword parameter you can
replace the simple name with a list consisting of the name of the
parameter, a default value, and another parameter name, called a
supplied-p parameter. The supplied-p parameter will be set to true or
false depending on whether an argument was actually passed for that
keyword parameter in a particular call to the function. Here's a
version of foo that uses this feature:
(defun foo (&key a (b 20) (c 30 c-p)) (list a b c c-p))
Now the same calls from earlier yield these results:
(foo :a 1 :b 2 :c 3) ==> (1 2 3 T)
(foo :c 3 :b 2 :a 1) ==> (1 2 3 T)
(foo :a 1 :c 3) ==> (1 20 3 T)
(foo) ==> (NIL 20 30 NIL)
The general selector-function generator, which you can call where for
reasons that will soon become apparent if you're familiar with SQL
databases, is a function that takes four keyword parameters
corresponding to the fields in our CD records and generates a selector
function that selects any CDs that match all the values given to where.
For instance, it will let you say things like this:
(select (where :artist "Dixie Chicks"))
or this:
(select (where :rating 10 :ripped nil))
The function looks like this:
(defun where (&key title artist rating (ripped nil ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) t)
(if artist (equal (getf cd :artist) artist) t)
(if rating (equal (getf cd :rating) rating) t)
(if ripped-p (equal (getf cd :ripped) ripped) t))))
This function returns an anonymous function that returns the logical
AND of one clause per field in our CD records. Each clause checks if
the appropriate argument was passed in and then either compares it to
the value in the corresponding field in the CD record or returns t,
Lisp's version of truth, if the parameter wasn't passed in. Thus, the
selector function will return t only for CDs that match all the
arguments passed to where. (3) Note that you need to use a three-item
list to specify the keyword parameter ripped because you need to know
whether the caller actually passed :ripped nil, meaning, "Select CDs
whose ripped field is nil," or whether they left out :ripped altogether,
meaning "I don't care what the value of the ripped field is."
---------- Footnotes ----------
(1) The word lambda is used in Lisp because of an early connection to
the lambda calculus, a mathematical formalism invented for studying
mathematical functions.
(2) The technical term for a function that references a variable in
its enclosing scope is a closure because the function "closes over" the
variable. I'll discuss closures in more detail in Chapter 6.
(3) Note that in Lisp, an IF form, like everything else, is an
expression that returns a value. It's actually more like the ternary
operator (?:) in Perl, Java, and C in that this is legal in those
languages:
some_var = some_boolean ? value1 : value2;
while this isn't:
some_var = if (some_boolean) value1; else value2;
because in those languages, if is a statement, not an expression.
File: pcl.info, Node: 3-7, Next: 3-8, Prev: 3-6, Up: Chapter 3
Updating Existing Records-Another Use for WHERE
===============================================
Now that you've got nice generalized select and where functions, you're
in a good position to write the next feature that every database needs-a
way to update particular records. In SQL the update command is used to
update a set of records matching a particular where clause. That seems
like a good model, especially since you've already got a where-clause
generator. In fact, the update function is mostly just the application
of a few ideas you've already seen: using a passed-in selector function
to choose the records to update and using keyword arguments to specify
the values to change. The main new bit is the use of a function MAPCAR
that maps over a list, *db* in this case, and returns a new list
containing the results of calling a function on each item in the
original list.
(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
(setf *db*
(mapcar
#'(lambda (row)
(when (funcall selector-fn row)
(if title (setf (getf row :title) title))
(if artist (setf (getf row :artist) artist))
(if rating (setf (getf row :rating) rating))
(if ripped-p (setf (getf row :ripped) ripped)))
row) *db*)))
One other new bit here is the use of SETF on a complex form such as
(getf row :title). I'll discuss SETF in greater detail in Chapter 6,
but for now you just need to know that it's a general assignment
operator that can be used to assign lots of "places" other than just
variables. (It's a coincidence that SETF and GETF have such similar
names-they don't have any special relationship.) For now it's enough to
know that after (setf (getf row :title) title), the plist referenced by
row will have the value of the variable title following the property
name :title. With this update function if you decide that you really
dig the Dixie Chicks and that all their albums should go to 11, you can
evaluate the following form:
CL-USER> (update (where :artist "Dixie Chicks") :rating 11)
NIL
And it is so.
CL-USER> (select (where :artist "Dixie Chicks"))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 11 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 11 :RIPPED T))
You can even more easily add a function to delete rows from the
database.
(defun delete-rows (selector-fn)
(setf *db* (remove-if selector-fn *db*)))
The function REMOVE-IF is the complement of REMOVE-IF-NOT; it returns
a list with all the elements that do match the predicate removed. Like
REMOVE-IF-NOT, it doesn't actually affect the list it's passed but by
saving the result back into *db*, delete-rows (1) actually changes the
contents of the database. (2)
---------- Footnotes ----------
(1) You need to use the name delete-rows rather than the more obvious
delete because there's already a function in Common Lisp called DELETE.
The Lisp package system gives you a way to deal with such naming
conflicts, so you could have a function named delete if you wanted. But
I'm not ready to explain packages just yet.
(2) If you're worried that this code creates a memory leak, rest
assured: Lisp was the language that invented garbage collection (and
heap allocation for that matter). The memory used by the old value of
*db* will be automatically reclaimed, assuming no one else is holding on
to a reference to it, which none of this code is.
File: pcl.info, Node: 3-8, Next: 3-9, Prev: 3-7, Up: Chapter 3
Removing Duplication and Winning Big
====================================
So far all the database code supporting insert, select, update, and
delete, not to mention a command-line user interface for adding new
records and dumping out the contents, is just a little more than 50
lines. Total. (1)
Yet there's still some annoying code duplication. And it turns out
you can remove the duplication and make the code more flexible at the
same time. The duplication I'm thinking of is in the where function.
The body of the where function is a bunch of clauses like this, one per
field:
(if title (equal (getf cd :title) title) t)
Right now it's not so bad, but like all code duplication it has the
same cost: if you want to change how it works, you have to change
multiple copies. And if you change the fields in a CD, you'll have to
add or remove clauses to where. And update suffers from the same kind
of duplication. It's doubly annoying since the whole point of the where
function is to dynamically generate a bit of code that checks the values
you care about; why should it have to do work at runtime checking
whether title was even passed in?
Imagine that you were trying to optimize this code and discovered
that it was spending too much time checking whether title and the rest
of the keyword parameters to where were even set? (2) If you really
wanted to remove all those runtime checks, you could go through a
program and find all the places you call where and look at exactly what
arguments you're passing. Then you could replace each call to where
with an anonymous function that does only the computation necessary.
For instance, if you found this snippet of code:
(select (where :title "Give Us a Break" :ripped t))
you could change it to this:
(select
#'(lambda (cd)
(and (equal (getf cd :title) "Give Us a Break")
(equal (getf cd :ripped) t))))
Note that the anonymous function is different from the one that where
would have returned; you're not trying to save the call to where but
rather to provide a more efficient selector function. This anonymous
function has clauses only for the fields that you actually care about at
this call site, so it doesn't do any extra work the way a function
returned by where might.
You can probably imagine going through all your source code and
fixing up all the calls to where in this way. But you can probably also
imagine that it would be a huge pain. If there were enough of them, and
it was important enough, it might even be worthwhile to write some kind
of preprocessor that converts where calls to the code you'd write by
hand.
The Lisp feature that makes this trivially easy is its macro system.
I can't emphasize enough that the Common Lisp macro shares essentially
nothing but the name with the text-based macros found in C and C++.
Where the C pre-processor operates by textual substitution and
understands almost nothing of the structure of C and C++, a Lisp macro
is essentially a code generator that gets run for you automatically by
the compiler. (3) When a Lisp expression contains a call to a macro,
instead of evaluating the arguments and passing them to the function,
the Lisp compiler passes the arguments, unevaluated, to the macro code,
which returns a new Lisp expression that is then evaluated in place of
the original macro call.
I'll start with a simple, and silly, example and then show how you
can replace the where function with a where macro. Before I can write
this example macro, I need to quickly introduce one new function:
REVERSE takes a list as an argument and returns a new list that is its
reverse. So (reverse '(1 2 3)) evaluates to (3 2 1). Now let's create
a macro.
(defmacro backwards (expr) (reverse expr))
The main syntactic difference between a function and a macro is that
you define a macro with DEFMACRO instead of DEFUN. After that a macro
definition consists of a name, just like a function, a parameter list,
and a body of expressions, both also like a function. However, a macro
has a totally different effect. You can use this macro as follows:
CL-USER> (backwards ("hello, world" t format))
hello, world
NIL
How did that work? When the REPL started to evaluate the backwards
expression, it recognized that backwards is the name of a macro. So it
left the expression ("hello, world" t format) unevaluated, which is good
because it isn't a legal Lisp form. It then passed that list to the
backwards code. The code in backwards passed the list to REVERSE, which
returned the list (format t "hello, world"). backwards then passed that
value back out to the REPL, which then evaluated it in place of the
original expression.
The backwards macro thus defines a new language that's a lot like
Lisp-just backward-that you can drop into anytime simply by wrapping a
backward Lisp expression in a call to the backwards macro. And, in a
compiled Lisp program, that new language is just as efficient as normal
Lisp because all the macro code-the code that generates the new
expression-runs at compile time. In other words, the compiler will
generate exactly the same code whether you write (backwards ("hello,
world" t format)) or (format t "hello, world").
So how does that help with the code duplication in where? Well, you
can write a macro that generates exactly the code you need for each
particular call to where. Again, the best approach is to build our code
bottom up. In the hand-optimized selector function, you had an
expression of the following form for each actual field referred to in
the original call to where:
(equal (getf cd field) value)
So let's write a function that, given the name of a field and a
value, returns such an expression. Since an expression is just a list,
you might think you could write something like this:
(defun make-comparison-expr (field value) ; wrong
(list equal (list getf cd field) value))
However, there's one trick here: as you know, when Lisp sees a simple
name such as field or value other than as the first element of a list,
it assumes it's the name of a variable and looks up its value. That's
fine for field and value; it's exactly what you want. But it will treat
equal, getf, and cd the same way, which isn't what you want. However,
you also know how to stop Lisp from evaluating a form: stick a single
forward quote (') in front of it. So if you write make-comparison-expr
like this, it will do what you want:
(defun make-comparison-expr (field value)
(list 'equal (list 'getf 'cd field) value))
You can test it out in the REPL.
CL-USER> (make-comparison-expr :rating 10)
(EQUAL (GETF CD :RATING) 10)
CL-USER> (make-comparison-expr :title "Give Us a Break")
(EQUAL (GETF CD :TITLE) "Give Us a Break")
It turns out that there's an even better way to do it. What you'd
really like is a way to write an expression that's mostly not evaluated
and then have some way to pick out a few expressions that you do want
evaluated. And, of course, there's just such a mechanism. A back quote
(') before an expression stops evaluation just like a forward quote.
CL-USER> `(1 2 3)
(1 2 3)
CL-USER> '(1 2 3)
(1 2 3)
However, in a back-quoted expression, any subexpression that's
preceded by a comma is evaluated. Notice the effect of the comma in the
second expression:
`(1 2 (+ 1 2)) ==> (1 2 (+ 1 2))
`(1 2 ,(+ 1 2)) ==> (1 2 3)
Using a back quote, you can write make-comparison-expr like this:
(defun make-comparison-expr (field value)
`(equal (getf cd ,field) ,value))
Now if you look back to the hand-optimized selector function, you can
see that the body of the function consisted of one comparison expression
per field/value pair, all wrapped in an AND expression. Assume for the
moment that you'll arrange for the arguments to the where macro to be
passed as a single list. You'll need a function that can take the
elements of such a list pairwise and collect the results of calling
make-comparison-expr on each pair. To implement that function, you can
dip into the bag of advanced Lisp tricks and pull out the mighty and
powerful LOOP macro.
(defun make-comparisons-list (fields)
(loop while fields
collecting (make-comparison-expr (pop fields) (pop fields))))
A full discussion of LOOP will have to wait until Chapter 22; for now
just note that this LOOP expression does exactly what you need: it loops
while there are elements left in the fields list, popping off two at a
time, passing them to make-comparison-expr, and collecting the results
to be returned at the end of the loop. The POP macro performs the
inverse operation of the PUSH macro you used to add records to *db*.
Now you just need to wrap up the list returned by
make-comparison-list in an AND and an anonymous function, which you can
do in the where macro itself. Using a back quote to make a template
that you fill in by interpolating the value of make-comparisons-list,
it's trivial.
(defmacro where (&rest clauses)
`#'(lambda (cd) (and ,@(make-comparisons-list clauses))))
This macro uses a variant of , (namely, the ,@) before the call to
make-comparisons-list. The ,@ "splices" the value of the following
expression-which must evaluate to a list-into the enclosing list. You
can see the difference between , and , in the following two expressions:
`(and ,(list 1 2 3)) ==> (AND (1 2 3))
`(and ,@(list 1 2 3)) ==> (AND 1 2 3)
You can also use , to splice into the middle of a list.
`(and ,@(list 1 2 3) 4) ==> (AND 1 2 3 4)
The other important feature of the where macro is the use of &rest in
the argument list. Like &key, &rest modifies the way arguments are
parsed. With a &rest in its parameter list, a function or macro can
take an arbitrary number of arguments, which are collected into a single
list that becomes the value of the variable whose name follows the
&rest. So if you call where like this:
(where :title "Give Us a Break" :ripped t)
the variable clauses will contain the list.
(:title "Give Us a Break" :ripped t)
This list is passed to make-comparisons-list, which returns a list of
comparison expressions. You can see exactly what code a call to where
will generate using the function MACROEXPAND-1. If you pass
MACROEXPAND-1, a form representing a macro call, it will call the macro
code with appropriate arguments and return the expansion. So you can
check out the previous where call like this:
CL-USER> (macroexpand-1 '(where :title "Give Us a Break" :ripped t))
#'(LAMBDA (CD)
(AND (EQUAL (GETF CD :TITLE) "Give Us a Break")
(EQUAL (GETF CD :RIPPED) T)))
T
Looks good. Let's try it for real.
CL-USER> (select (where :title "Give Us a Break" :ripped t))
((:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T))
It works. And the where macro with its two helper functions is
actually one line shorter than the old where function. And it's more
general in that it's no longer tied to the specific fields in our CD
records.
---------- Footnotes ----------
(1) A friend of mine was once interviewing an engineer for a
programming job and asked him a typical interview question: how do you
know when a function or method is too big? Well, said the candidate, I
don't like any method to be bigger than my head. You mean you can't
keep all the details in your head? No, I mean I put my head up against
my monitor, and the code shouldn't be bigger than my head.
(2) It's unlikely that the cost of checking whether keyword
parameters had been passed would be a detectible drag on performance
since checking whether a variable is NIL is going to be pretty cheap.
On the other hand, these functions returned by where are going to be
right in the middle of the inner loop of any select, update, or
delete-rows call, as they have to be called once per entry in the
database. Anyway, for illustrative purposes, this will have to do.
(3) Macros are also run by the interpreter-however, it's easier to
understand the point of macros when you think about compiled code. As
with everything else in this chapter, I'll cover this in greater detail
in future chapters.
File: pcl.info, Node: 3-9, Next: Chapter 4, Prev: 3-7, Up: Chapter 3
Wrapping Up
===========
Now, an interesting thing has happened. You removed duplication and
made the code more efficient and more general at the same time. That's
often the way it goes with a well-chosen macro. This makes sense
because a macro is just another mechanism for creating
abstractions-abstraction at the syntactic level, and abstractions are by
definition more concise ways of expressing underlying generalities. Now
the only code in the mini-database that's specific to CDs and the fields
in them is in the make-cd, prompt-for-cd, and add-cd functions. In
fact, our new where macro would work with any plist-based database.
However, this is still far from being a complete database. You can
probably think of plenty of features to add, such as supporting multiple
tables or more elaborate queries. In Chapter 27 we'll build an MP3
database that incorporates some of those features.
The point of this chapter was to give you a quick introduction to
just a handful of Lisp's features and show how they're used to write
code that's a bit more interesting than "hello, world." In the next
chapter we'll begin a more systematic overview of Lisp.
File: pcl.info, Node: Chapter 4, Next: Chapter 5, Prev: Chapter 3, Up: Top
4. Syntax and Semantics
=======================
After that whirlwind tour, we'll settle down for a few chapters to take
a more systematic look at the features you've used so far. I'll start
with an overview of the basic elements of Lisp's syntax and semantics,
which means, of course, that I must first address that burning question.
. .
* Menu:
* 4-1:: What's with All the Parentheses?
* 4-2:: Breaking Open the Black Box
* 4-3:: S-expressions
* 4-4:: S-expressions As Lisp Forms
* 4-5:: Function Calls
* 4-6:: Special Operators
* 4-7:: Macros
* 4-8:: Truth, Falsehood, and Equality
* 4-9:: Formatting Lisp Code
File: pcl.info, Node: 4-1, Next: 4-2, Prev: Chapter 4, Up: Chapter 4
What's with All the Parentheses?
================================
Lisp's syntax is quite a bit different from the syntax of languages
descended from Algol. The two most immediately obvious characteristics
are the extensive use of parentheses and prefix notation. For whatever
reason, a lot of folks are put off by this syntax. Lisp's detractors
tend to describe the syntax as "weird" and "annoying." Lisp, they say,
must stand for Lots of Irritating Superfluous Parentheses. Lisp folks,
on the other hand, tend to consider Lisp's syntax one of its great
virtues. How is it that what's so off-putting to one group is a source
of delight to another?
I can't really make the complete case for Lisp's syntax until I've
explained Lisp's macros a bit more thoroughly, but I can start with an
historical tidbit that suggests it may be worth keeping an open mind:
when John McCarthy first invented Lisp, he intended to implement a more
Algol-like syntax, which he called M-expressions. However, he never got
around to it. He explained why not in his article "History of Lisp."
(1)
The project of defining M-expressions precisely and compiling them
or at least translating them into S-expressions was neither
finalized nor explicitly abandoned. It just receded into the
indefinite future, and a new generation of programmers appeared who
preferred [S-expressions] to any FORTRAN-like or ALGOL-like
notation that could be devised.
In other words, the people who have actually used Lisp over the past
45 years have liked the syntax and have found that it makes the language
more powerful. In the next few chapters, you'll begin to see why.
---------- Footnotes ----------
(1) http://www-formal.stanford.edu/jmc/history/lisp/node3.html
File: pcl.info, Node: 4-2, Next: 4-3, Prev: 4-1, Up: Chapter 4
Breaking Open the Black Box
===========================
Before we look at the specifics of Lisp's syntax and semantics, it's
worth taking a moment to look at how they're defined and how this
differs from many other languages.
In most programming languages, the language processor-whether an
interpreter or a compiler-operates as a black box: you shove a sequence
of characters representing the text of a program into the black box, and
it-depending on whether it's an interpreter or a compiler-either
executes the behaviors indicated or produces a compiled version of the
program that will execute the behaviors when it's run.
Inside the black box, of course, language processors are usually
divided into subsystems that are each responsible for one part of the
task of translating a program text into behavior or object code. A
typical division is to split the processor into three phases, each of
which feeds into the next: a lexical analyzer breaks up the stream of
characters into tokens and feeds them to a parser that builds a tree
representing the expressions in the program, according to the language's
grammar. This tree-called an abstract syntax tree-is then fed to an
evaluator that either interprets it directly or compiles it into some
other language such as machine code. Because the language processor is
a black box, the data structures used by the processor, such as the
tokens and abstract syntax trees, are of interest only to the language
implementer.
In Common Lisp things are sliced up a bit differently, with
consequences for both the implementer and for how the language is
defined. Instead of a single black box that goes from text to program
behavior in one step, Common Lisp defines two black boxes, one that
translates text into Lisp objects and another that implements the
semantics of the language in terms of those objects. The first box is
called the reader, and the second is called the evaluator. (1)
Each black box defines one level of syntax. The reader defines how
strings of characters can be translated into Lisp objects called
s-expressions. (2) Since the s-expression syntax includes syntax for
lists of arbitrary objects, including other lists, s-expressions can
represent arbitrary tree expressions, much like the abstract syntax tree
generated by the parsers for non-Lisp languages.
The evaluator then defines a syntax of Lisp forms that can be built
out of s-expressions. Not all s-expressions are legal Lisp forms any
more than all sequences of characters are legal s-expressions. For
instance, both (foo 1 2) and ("foo" 1 2) are s-expressions, but only the
former can be a Lisp form since a list that starts with a string has no
meaning as a Lisp form.
This split of the black box has a couple of consequences. One is
that you can use s-expressions, as you saw in Chapter 3, as an
externalizable data format for data other than source code, using READ
to read it and PRINT to print it. (3) The other consequence is that
since the semantics of the language are defined in terms of trees of
objects rather than strings of characters, it's easier to generate code
within the language than it would be if you had to generate code as
text. Generating code completely from scratch is only marginally
easier-building up lists vs. building up strings is about the same
amount of work. The real win, however, is that you can generate code by
manipulating existing data. This is the basis for Lisp's macros, which
I'll discuss in much more detail in future chapters. For now I'll focus
on the two levels of syntax defined by Common Lisp: the syntax of
s-expressions understood by the reader and the syntax of Lisp forms
understood by the evaluator.
---------- Footnotes ----------
(1) Lisp implementers, like implementers of any language, have many
ways they can implement an evaluator, ranging from a "pure" interpreter
that interprets the objects given to the evaluator directly to a
compiler that translates the objects into machine code that it then
runs. In the middle are implementations that compile the input into an
intermediate form such as bytecodes for a virtual machine and then
interprets the bytecodes. Most Common Lisp implementations these days
use some form of compilation even when evaluating code at run time.
(2) Sometimes the phrase s-expression refers to the textual
representation and sometimes to the objects that result from reading the
textual representation. Usually either it's clear from context which is
meant or the distinction isn't that important.
(3) Not all Lisp objects can be written out in a way that can be read
back in. But anything you can READ can be printed back out "readably"
with PRINT.
File: pcl.info, Node: 4-3, Next: 4-4, Prev: 4-2, Up: Chapter 4
S-expressions
=============
The basic elements of s-expressions are lists and atoms. Lists are
delimited by parentheses and can contain any number of
whitespace-separated elements. Atoms are everything else. (1) The
elements of lists are themselves s-expressions (in other words, atoms or
nested lists). Comments-which aren't, technically speaking,
s-expressions-start with a semicolon, extend to the end of a line, and
are treated essentially like whitespace.
And that's pretty much it. Since lists are syntactically so trivial,
the only remaining syntactic rules you need to know are those governing
the form of different kinds of atoms. In this section I'll describe the
rules for the most commonly used kinds of atoms: numbers, strings, and
names. After that, I'll cover how s-expressions composed of these
elements can be evaluated as Lisp forms.
Numbers are fairly straightforward: any sequence of digits-possibly
prefaced with a sign (+ or -), containing a decimal point (.) or a
solidus (/), or ending with an exponent marker-is read as a number. For
example:
123 ; the integer one hundred twenty-three
3/7 ; the ratio three-sevenths
1.0 ; the floating-point number one in default precision
1.0e0 ; another way to write the same floating-point number
1.0d0 ; the floating-point number one in "double" precision
1.0e-4 ; the floating-point equivalent to one-ten-thousandth
+42 ; the integer forty-two
-42 ; the integer negative forty-two
-1/4 ; the ratio negative one-quarter
-2/8 ; another way to write negative one-quarter
246/2 ; another way to write the integer one hundred twenty-three
These different forms represent different kinds of numbers: integers,
ratios, and floating point. Lisp also supports complex numbers, which
have their own notation and which I'll discuss in Chapter 10.
As some of these examples suggest, you can notate the same number in
many ways. But regardless of how you write them, all rationals-integers
and ratios-are represented internally in "simplified" form. In other
words, the objects that represent -2/8 or 246/2 aren't distinct from the
objects that represent -1/4 and 123. Similarly, 1.0 and 1.0e0 are just
different ways of writing the same number. On the other hand, 1.0,
1.0d0, and 1 can all denote different objects because the different
floating-point representations and integers are different types. We'll
save the details about the characteristics of different kinds of numbers
for Chapter 10.
Strings literals, as you saw in the previous chapter, are enclosed in
double quotes. Within a string a backslash (\) escapes the next
character, causing it to be included in the string regardless of what it
is. The only two characters that must be escaped within a string are
double quotes and the backslash itself. All other characters can be
included in a string literal without escaping, regardless of their
meaning outside a string. Some example string literals are as follows:
"foo" ; the string containing the characters f, o, and o.
"fo\o" ; the same string
"fo\\o" ; the string containing the characters f, o, \, and o.
"fo\"o" ; the string containing the characters f, o, ", and o.
Names used in Lisp programs, such as FORMAT and hello-world, and *db*
are represented by objects called symbols. The reader knows nothing
about how a given name is going to be used-whether it's the name of a
variable, a function, or something else. It just reads a sequence of
characters and builds an object to represent the name. (2) Almost any
character can appear in a name. Whitespace characters can't, though,
because the elements of lists are separated by whitespace. Digits can
appear in names as long as the name as a whole can't be interpreted as a
number. Similarly, names can contain periods, but the reader can't read
a name that consists only of periods. Ten characters that serve other
syntactic purposes can't appear in names: open and close parentheses,
double and single quotes, backtick, comma, colon, semicolon, backslash,
and vertical bar. And even those characters can, if you're willing to
escape them by preceding the character to be escaped with a backslash or
by surrounding the part of the name containing characters that need
escaping with vertical bars.
Two important characteristics of the way the reader translates names
to symbol objects have to do with how it treats the case of letters in
names and how it ensures that the same name is always read as the same
symbol. While reading names, the reader converts all unescaped
characters in a name to their uppercase equivalents. Thus, the reader
will read foo, Foo, and FOO as the same symbol: FOO. However, \f\o\o and
|foo| will both be read as foo, which is a different object than the
symbol FOO. This is why when you define a function at the REPL and it
prints the name of the function, it's been converted to uppercase.
Standard style, these days, is to write code in all lowercase and let
the reader change names to uppercase. (3)
To ensure that the same textual name is always read as the same
symbol, the reader interns symbols-after it has read the name and
converted it to all uppercase, the reader looks in a table called a
package for an existing symbol with the same name. If it can't find
one, it creates a new symbol and adds it to the table. Otherwise, it
returns the symbol already in the table. Thus, anywhere the same name
appears in any s-expression, the same object will be used to represent
it. (4)
Because names can contain many more characters in Lisp than they can
in Algol-derived languages, certain naming conventions are distinct to
Lisp, such as the use of hyphenated names like hello-world. Another
important convention is that global variables are given names that start
and end with *. Similarly, constants are given names starting and
ending in +. And some programmers will name particularly low-level
functions with names that start with % or even %%. The names defined in
the language standard use only the alphabetic characters (A-Z) plus *,
+, -, /, 1, 2, <, =, >, and &.
The syntax for lists, numbers, strings, and symbols can describe a
good percentage of Lisp programs. Other rules describe notations for
literal vectors, individual characters, and arrays, which I'll cover
when I talk about the associated data types in Chapters 10 and 11. For
now the key thing to understand is how you can combine numbers, strings,
and symbols with parentheses-delimited lists to build s-expressions
representing arbitrary trees of objects. Some simple examples look like
this:
x ; the symbol X
() ; the empty list
(1 2 3) ; a list of three numbers
("foo" "bar") ; a list of two strings
(x y z) ; a list of three symbols
(x 1 "foo") ; a list of a symbol, a number, and a string
(+ (* 2 3) 4) ; a list of a symbol, a list, and a number.
An only slightly more complex example is the following four-item list
that contains two symbols, the empty list, and another list, itself
containing two symbols and a string:
(defun hello-world ()
(format t "hello, world"))
---------- Footnotes ----------
(1) The empty list, (), which can also be written NIL, is both an
atom and a list.
(2) In fact, as you'll see later, names aren't intrinsically tied to
any one kind of thing. You can use the same name, depending on context,
to refer to both a variable and a function, not to mention several other
possibilities.
(3) The case-converting behavior of the reader can, in fact, be
customized, but understanding when and how to change it requires a much
deeper discussion of the relation between names, symbols, and other
program elements than I'm ready to get into just yet.
(4) I'll discuss the relation between symbols and packages in more
detail in Chapter 21.
File: pcl.info, Node: 4-4, Next: 4-5, Prev: 4-3, Up: Chapter 4
S-expressions As Lisp Forms
===========================
After the reader has translated a bunch of text into s-expressions, the
s-expressions can then be evaluated as Lisp code. Or some of them
can-not every s-expressions that the reader can read can necessarily be
evaluated as Lisp code. Common Lisp's evaluation rule defines a second
level of syntax that determines which s-expressions can be treated as
Lisp forms. (1) The syntactic rules at this level are quite simple.
Any atom-any nonlist or the empty list-is a legal Lisp form as is any
list that has a symbol as its first element. (2)
Of course, the interesting thing about Lisp forms isn't their syntax
but how they're evaluated. For purposes of discussion, you can think of
the evaluator as a function that takes as an argument a syntactically
well-formed Lisp form and returns a value, which we can call the value
of the form. Of course, when the evaluator is a compiler, this is a bit
of a simplification-in that case, the evaluator is given an expression
and generates code that will compute the appropriate value when it's
run. But this simplification lets me describe the semantics of Common
Lisp in terms of how the different kinds of Lisp forms are evaluated by
this notional function.
The simplest Lisp forms, atoms, can be divided into two categories:
symbols and everything else. A symbol, evaluated as a form, is
considered the name of a variable and evaluates to the current value of
the variable. (3) I'll discuss in Chapter 6 how variables get their
values in the first place. You should also note that certain
"variables" are that old oxymoron of programming: "constant variables."
For instance, the symbol PI names a constant variable whose value is the
best possible floating-point approximation to the mathematical constant
pi.
All other atoms-numbers and strings are the kinds you've seen so
far-are self-evaluating objects. This means when such an expression is
passed to the notional evaluation function, it's simply returned. You
saw examples of self-evaluating objects in Chapter 2 when you typed 10
and "hello, world" at the REPL.
It's also possible for symbols to be self-evaluating in the sense
that the variables they name can be assigned the value of the symbol
itself. Two important constants that are defined this way are T and
NIL, the canonical true and false values. I'll discuss their role as
booleans in the section "Truth, Falsehood, and Equality."
Another class of self-evaluating symbols are the keyword
symbols-symbols whose names start with :. When the reader interns such
a name, it automatically defines a constant variable with the name and
with the symbol as the value.
Things get more interesting when we consider how lists are evaluated.
All legal list forms start with a symbol, but three kinds of list forms
are evaluated in three quite different ways. To determine what kind of
form a given list is, the evaluator must determine whether the symbol
that starts the list is the name of a function, a macro, or a special
operator. If the symbol hasn't been defined yet-as may be the case if
you're compiling code that contains references to functions that will be
defined later-it's assumed to be a function name. (4) I'll refer to the
three kinds of forms as function call forms, macro forms, and special
forms.
---------- Footnotes ----------
(1) Of course, other levels of correctness exist in Lisp, as in other
languages. For instance, the s-expression that results from reading
(foo 1 2) is syntactically well-formed but can be evaluated only if foo
is the name of a function or macro.
(2) One other rarely used kind of Lisp form is a list whose first
element is a lambda form. I'll discuss this kind of form in Chapter 5.
(3) One other possibility exists-it's possible to define symbol
macros that are evaluated slightly differently. We won't worry about
them.
(4) In Common Lisp a symbol can name both an operator-function,
macro, or special operator-and a variable. This is one of the major
differences between Common Lisp and Scheme. The difference is sometimes
described as Common Lisp being a Lisp-2 vs. Scheme being a Lisp-1-a
Lisp-2 has two namespaces, one for operators and one for variables, but
a Lisp-1 uses a single namespace. Both choices have advantages, and
partisans can debate endlessly which is better.
File: pcl.info, Node: 4-5, Next: 4-6, Prev: 4-4, Up: Chapter 4
Function Calls
==============
The evaluation rule for function call forms is simple: evaluate the
remaining elements of the list as Lisp forms and pass the resulting
values to the named function. This rule obviously places some
additional syntactic constraints on a function call form: all the
elements of the list after the first must themselves be well-formed Lisp
forms. In other words, the basic syntax of a function call form is as
follows, where each of the arguments is itself a Lisp form:
(function-name argument*)
Thus, the following expression is evaluated by first evaluating 1,
then evaluating 2, and then passing the resulting values to the +
function, which returns 3:
(+ 1 2)
A more complex expression such as the following is evaluated in
similar fashion except that evaluating the arguments (+ 1 2) and (- 3 4)
entails first evaluating their arguments and applying the appropriate
functions to them:
(* (+ 1 2) (- 3 4))
Eventually, the values 3 and -1 are passed to the * function, which
returns -3.
As these examples show, functions are used for many of the things
that require special syntax in other languages. This helps keep Lisp's
syntax regular.
File: pcl.info, Node: 4-6, Next: 4-7, Prev: 4-5, Up: Chapter 4
Special Operators
=================
That said, not all operations can be defined as functions. Because all
the arguments to a function are evaluated before the function is called,
there's no way to write a function that behaves like the IF operator you
used in Chapter 3. To see why, consider this form:
(if x (format t "yes") (format t "no"))
If IF were a function, the evaluator would evaluate the argument
expressions from left to right. The symbol x would be evaluated as a
variable yielding some value; then (format t "yes") would be evaluated
as a function call, yielding NIL after printing "yes" to standard
output. Then (format t "no") would be evaluated, printing "no" and also
yielding NIL. Only after all three expressions were evaluated would the
resulting values be passed to IF, too late for it to control which of
the two FORMAT expressions gets evaluated.
To solve this problem, Common Lisp defines a couple dozen so-called
special operators, IF being one, that do things that functions can't do.
There are 25 in all, but only a small handful are used directly in
day-to-day programming. (1)
When the first element of a list is a symbol naming a special
operator, the rest of the expressions are evaluated according to the
rule for that operator.
The rule for IF is pretty easy: evaluate the first expression. If it
evaluates to non-NIL, then evaluate the next expression and return its
value. Otherwise, return the value of evaluating the third expression
or NIL if the third expression is omitted. In other words, the basic
form of an IF expression is as follows:
(if test-form then-form [ else-form ])
The test-form will always be evaluated and then one or the other of
the then-form or else-form.
An even simpler special operator is QUOTE, which takes a single
expression as its "argument" and simply returns it, unevaluated. For
instance, the following evaluates to the list (+ 1 2), not the value 3:
(quote (+ 1 2))
There's nothing special about this list; you can manipulate it just
like any list you could create with the LIST function. (2)
QUOTE is used commonly enough that a special syntax for it is built
into the reader. Instead of writing the following:
(quote (+ 1 2))
you can write this:
'(+ 1 2)
This syntax is a small extension of the s-expression syntax
understood by the reader. From the point of view of the evaluator, both
those expressions will look the same: a list whose first element is the
symbol QUOTE and whose second element is the list (+ 1 2). (3)
In general, the special operators implement features of the language
that require some special processing by the evaluator. For instance,
several special operators manipulate the environment in which other
forms will be evaluated. One of these, which I'll discuss in detail in
Chapter 6, is LET, which is used to create new variable bindings. The
following form evaluates to 10 because the second x is evaluated in an
environment where it's the name of a variable established by the LET
with the value 10:
(let ((x 10)) x)
---------- Footnotes ----------
(1) The others provide useful, but somewhat esoteric, features. I'll
discuss them as the features they support come up.
(2) Well, one difference exists-literal objects such as quoted lists,
but also including double-quoted strings, literal arrays, and vectors
(whose syntax you'll see later), must not be modified. Consequently,
any lists you plan to manipulate you should create with LIST.
(3) This syntax is an example of a reader macro. Reader macros
modify the syntax the reader uses to translate text into Lisp objects.
It is, in fact, possible to define your own reader macros, but that's a
rarely used facility of the language. When most Lispers talk about
"extending the syntax" of the language, they're talking about regular
macros, as I'll discuss in a moment.
File: pcl.info, Node: 4-7, Next: 4-8, Prev: 4-6, Up: Chapter 4
Macros
======
While special operators extend the syntax of Common Lisp beyond what can
be expressed with just function calls, the set of special operators is
fixed by the language standard. Macros, on the other hand, give users
of the language a way to extend its syntax. As you saw in Chapter 3, a
macro is a function that takes s-expressions as arguments and returns a
Lisp form that's then evaluated in place of the macro form. The
evaluation of a macro form proceeds in two phases: First, the elements
of the macro form are passed, unevaluated, to the macro function.
Second, the form returned by the macro function-called its expansion-is
evaluated according to the normal evaluation rules.
It's important to keep the two phases of evaluating a macro form
clear in your mind. It's easy to lose track when you're typing
expressions at the REPL because the two phases happen one after another
and the value of the second phase is immediately returned. But when
Lisp code is compiled, the two phases happen at completely different
times, so it's important to keep clear what's happening when. For
instance, when you compile a whole file of source code with the function
COMPILE-FILE, all the macro forms in the file are recursively expanded
until the code consists of nothing but function call forms and special
forms. This macroless code is then compiled into a FASL file that the
LOAD function knows how to load. The compiled code, however, isn't
executed until the file is loaded. Because macros generate their
expansion at compile time, they can do relatively large amounts of work
generating their expansion without having to pay for it when the file is
loaded or the functions defined in the file are called.
Since the evaluator doesn't evaluate the elements of the macro form
before passing them to the macro function, they don't need to be
well-formed Lisp forms. Each macro assigns a meaning to the
s-expressions in the macro form by virtue of how it uses them to
generate its expansion. In other words, each macro defines its own
local syntax. For instance, the backwards macro from Chapter 3 defines
a syntax in which an expression is a legal backwards form if it's a list
that's the reverse of a legal Lisp form.
I'll talk quite a bit more about macros throughout this book. For
now the important thing for you to realize is that macros-while
syntactically similar to function calls-serve quite a different purpose,
providing a hook into the compiler. (1)
---------- Footnotes ----------
(1) People without experience using Lisp's macros or, worse yet,
bearing the scars of C preprocessor-inflicted wounds, tend to get
nervous when they realize that macro calls look like regular function
calls. This turns out not to be a problem in practice for several
reasons. One is that macro forms are usually formatted differently than
function calls. For instance, you write the following:
(dolist (x foo)
(print x))
rather than this:
(dolist (x foo) (print x))
or
(dolist (x foo)
(print x))
the way you would if DOLIST was a function. A good Lisp environment
will automatically format macro calls correctly, even for user-defined
macros.
And even if a DOLIST form was written on a single line, there are
several clues that it's a macro: For one, the expression (x foo) is
meaningful by itself only if x is the name of a function or macro.
Combine that with the later occurrence of x as a variable, and it's
pretty suggestive that DOLIST is a macro that's creating a binding for a
variable named x. Naming conventions also help-looping constructs,
which are invariably macros-are frequently given names starting with do.
File: pcl.info, Node: 4-8, Next: 4-9, Prev: 4-7, Up: Chapter 4
Truth, Falsehood, and Equality
==============================
Two last bits of basic knowledge you need to get under your belt are
Common Lisp's notion of truth and falsehood and what it means for two
Lisp objects to be "equal." Truth and falsehood are-in this
realm-straightforward: the symbol NIL is the only false value, and
everything else is true. The symbol T is the canonical true value and
can be used when you need to return a non-NIL value and don't have
anything else handy. The only tricky thing about NIL is that it's the
only object that's both an atom and a list: in addition to falsehood,
it's also used to represent the empty list. (1) This equivalence
between NIL and the empty list is built into the reader: if the reader
sees (), it reads it as the symbol NIL. They're completely
interchangeable. And because NIL, as I mentioned previously, is the
name of a constant variable with the symbol NIL as its value, the
expressions nil, (), 'nil, and '() all evaluate to the same thing-the
unquoted forms are evaluated as a reference to the constant variable
whose value is the symbol NIL, but in the quoted forms the QUOTE special
operator evaluates to the symbol directly. For the same reason, both t
and 't will evaluate to the same thing: the symbol T.
Using phrases such as "the same thing" of course begs the question of
what it means for two values to be "the same." As you'll see in future
chapters, Common Lisp provides a number of type-specific equality
predicates: = is used to compare numbers, CHAR= to compare characters,
and so on. In this section I'll discuss the four "generic" equality
predicates-functions that can be passed any two Lisp objects and will
return true if they're equivalent and false otherwise. They are, in
order of discrimination, EQ, EQL, EQUAL, and EQUALP.
EQ tests for "object identity"-two objects are EQ if they're
identical. Unfortunately, the object identity of numbers and characters
depends on how those data types are implemented in a particular Lisp.
Thus, EQ may consider two numbers or two characters with the same value
to be equivalent, or it may not. Implementations have enough leeway
that the expression (eq 3 3) can legally evaluate to either true or
false. More to the point, (eq x x) can evaluate to either true or false
if the value of x happens to be a number or character.
Thus, you should never use EQ to compare values that may be numbers
or characters. It may seem to work in a predictable way for certain
values in a particular implementation, but you have no guarantee that it
will work the same way if you switch implementations. And switching
implementations may mean simply upgrading your implementation to a new
version-if your Lisp implementer changes how they represent numbers or
characters, the behavior of EQ could very well change as well.
Thus, Common Lisp defines EQL to behave like EQ except that it also
is guaranteed to consider two objects of the same class representing the
same numeric or character value to be equivalent. Thus, (eql 1 1) is
guaranteed to be true. And (eql 1 1.0) is guaranteed to be false since
the integer value 1 and the floating-point value are instances of
different classes.
There are two schools of thought about when to use EQ and when to use
EQL: The "use EQ when possible" camp argues you should use EQ when you
know you aren't going to be com-paring numbers or characters because (a)
it's a way to indicate that you aren't going to be comparing numbers or
characters and (b) it will be marginally more efficient since EQ doesn't
have to check whether its arguments are numbers or characters.
The "always use EQL" camp says you should never use EQ because (a)
the potential gain in clarity is lost because every time someone reading
your code-including you-sees an EQ, they have to stop and check whether
it's being used correctly (in other words, that it's never going to be
called upon to compare numbers or characters) and (b) that the
efficiency difference between EQ and EQL is in the noise compared to
real performance bottlenecks.
The code in this book is written in the "always use EQL" style. (2)
The other two equality predicates, EQUAL and EQUALP, are general in
the sense that they can operate on all types of objects, but they're
much less fundamental than EQ or EQL. They each define a slightly less
discriminating notion of equivalence than EQL, allowing different
objects to be considered equivalent. There's nothing special about the
particular notions of equivalence these functions implement except that
they've been found to be handy by Lisp programmers in the past. If
these predicates don't suit your needs, you can always define your own
predicate function that compares different types of objects in the way
you need.
EQUAL loosens the discrimination of EQL to consider lists equivalent
if they have the same structure and contents, recursively, according to
EQUAL. EQUAL also considers strings equivalent if they contain the same
characters. It also defines a looser definition of equivalence than EQL
for bit vectors and pathnames, two data types I'll discuss in future
chapters. For all other types, it falls back on EQL.
EQUALP is similar to EQUAL except it's even less discriminating. It
considers two strings equivalent if they contain the same characters,
ignoring differences in case. It also considers two characters
equivalent if they differ only in case. Numbers are equivalent under
EQUALP if they represent the same mathematical value. Thus, (equalp 1
1.0) is true. Lists with EQUALP elements are EQUALP; likewise, arrays
with EQUALP elements are EQUALP. As with EQUAL, there are a few other
data types that I haven't covered yet for which EQUALP can consider two
objects equivalent that neither EQL nor EQUAL will. For all other data
types, EQUALP falls back on EQL.
---------- Footnotes ----------
(1) Using the empty list as false is a reflection of Lisp's heritage
as a list-processing language much as the use of the integer 0 as false
in C is a reflection of its heritage as a bit-twiddling language. Not
all Lisps handle boolean values the same way. Another of the many
subtle differences upon which a good Common Lisp vs. Scheme flame war
can rage for days is Scheme's use of a distinct false value #f, which
isn't the same value as either the symbol nil or the empty list, which
are also distinct from each other.
(2) Even the language standard is a bit ambivalent about which of EQ
or EQL should be preferred. Object identity is defined by EQ, but the
standard defines the phrase the same when talking about objects to mean
EQL unless another predicate is explicitly mentioned. Thus, if you want
to be 100 percent technically correct, you can say that (- 3 2) and (- 4
3) evaluate to "the same" object but not that they evaluate to
"identical" objects. This is, admittedly, a bit of an
angels-on-pinheads kind of issue.
File: pcl.info, Node: 4-9, Next: Chapter 5, Prev: 4-7, Up: Chapter 4
Formatting Lisp Code
====================
While code formatting is, strictly speaking, neither a syntactic nor a
semantic matter, proper formatting is important to reading and writing
code fluently and idiomatically. The key to formatting Lisp code is to
indent it properly. The indentation should reflect the structure of the
code so that you don't need to count parentheses to see what goes with
what. In general, each new level of nesting gets indented a bit more,
and, if line breaks are necessary, items at the same level of nesting
are lined up. Thus, a function call that needs to be broken up across
multiple lines might be written like this:
(some-function arg-with-a-long-name
another-arg-with-an-even-longer-name)
Macro and special forms that implement control constructs are
typically indented a little differently: the "body" elements are
indented two spaces relative to the opening parenthesis of the form.
Thus:
(defun print-list (list)
(dolist (i list)
(format t "item: ~a~%" i)))
However, you don't need to worry too much about these rules because a
proper Lisp environment such as SLIME will take care of it for you. In
fact, one of the advantages of Lisp's regular syntax is that it's fairly
easy for software such as editors to know how to indent it. Since the
indentation is supposed to reflect the structure of the code and the
structure is marked by parentheses, it's easy to let the editor indent
your code for you.
In SLIME, hitting Tab at the beginning of each line will cause it to
be indented appropriately, or you can re-indent a whole expression by
positioning the cursor on the opening parenthesis and typing C-M-q. Or
you can re-indent the whole body of a function from anywhere within it
by typing C-c M-q.
Indeed, experienced Lisp programmers tend to rely on their editor
handling indenting automatically, not just to make their code look nice
but to detect typos: once you get used to how code is supposed to be
indented, a misplaced parenthesis will be instantly recognizable by the
weird indentation your editor gives you. For example, suppose you were
writing a function that was supposed to look like this:
(defun foo ()
(if (test)
(do-one-thing)
(do-another-thing)))
Now suppose you accidentally left off the closing parenthesis after
test. Because you don't bother counting parentheses, you quite likely
would have added an extra parenthesis at the end of the DEFUN form,
giving you this code:
(defun foo ()
(if (test
(do-one-thing)
(do-another-thing))))
However, if you had been indenting by hitting Tab at the beginning of
each line, you wouldn't have code like that. Instead you'd have this:
(defun foo ()
(if (test
(do-one-thing)
(do-another-thing))))
Seeing the then and else clauses indented way out under the condition
rather than just indented slightly relative to the IF shows you
immediately that something is awry.
Another important formatting rule is that closing parentheses are
always put on the same line as the last element of the list they're
closing. That is, don't write this:
(defun foo ()
(dotimes (i 10)
(format t "~d. hello~%" i)
)
)
but instead write this:
(defun foo ()
(dotimes (i 10)
(format t "~d. hello~%" i)))
The string of )))s at the end may seem forbidding, but as long your
code is properly indented the parentheses should fade away-no need to
give them undue prominence by spreading them across several lines.
Finally, comments should be prefaced with one to four semicolons
depending on the scope of the comment as follows:
;;;; Four semicolons are used for a file header comment.
;;; A comment with three semicolons will usually be a paragraph
;;; comment that applies to a large section of code that follows,
(defun foo (x)
(dotimes (i x)
;; Two semicolons indicate this comment applies to the code
;; that follows. Note that this comment is indented the same
;; as the code that follows.
(some-function-call)
(another i) ; this comment applies to this line only
(and-another) ; and this is for this line
(baz)))
Now you're ready to start looking in greater detail at the major
building blocks of Lisp programs, functions, variables, and macros. Up
next: functions.
File: pcl.info, Node: Chapter 5, Next: Chapter 6, Prev: Chapter 4, Up: Top
5. Functions
============
After the rules of syntax and semantics, the three most basic components
of all Lisp programs are functions, variables and macros. You used all
three while building the database in Chapter 3, but I glossed over a lot
of the details of how they work and how to best use them. I'll devote
the next few chapters to these three topics, starting with functions,
which-like their counterparts in other languages-provide the basic
mechanism for abstracting, well, functionality.
The bulk of Lisp itself consists of functions. More than three
quarters of the names defined in the language standard name functions.
All the built-in data types are defined purely in terms of what
functions operate on them. Even Lisp's powerful object system is built
upon a conceptual extension to functions, generic functions, which I'll
cover in Chapter 16.
And, despite the importance of macros to The Lisp Way, in the end all
real functionality is provided by functions. Macros run at compile
time, so the code they generate-the code that will actually make up the
program after all the macros are expanded-will consist entirely of calls
to functions and special operators. Not to mention, macros themselves
are also functions, albeit functions that are used to generate code
rather than to perform the actions of the program. (1)
* Menu:
* 5-1:: Defining New Functions
* 5-2:: Function Parameter Lists
* 5-3:: Optional Parameters
* 5-4:: Rest Parameters
* 5-5:: Keyword Parameters
* 5-6:: Mixing Different Parameter Types
* 5-7:: Function Return Values
* 5-8:: Functions As Data, a.k.a. Higher-Order Functions
* 5-9:: Anonymous Functions
---------- Footnotes ----------
(1) Despite the importance of functions in Common Lisp, it isn't
really accurate to describe it as a functional language. It's true some
of Common Lisp's features, such as its list manipulation functions, are
designed to be used in a body-form* style and that Lisp has a prominent
place in the history of functional programming-McCarthy introduced many
ideas that are now considered important in functional programming-but
Common Lisp was intentionally designed to support many different styles
of programming. In the Lisp family, Scheme is the nearest thing to a
"pure" functional language, and even it has several features that
disqualify it from absolute purity compared to languages such as Haskell
and ML.
File: pcl.info, Node: 5-1, Next: 5-2, Prev: Chapter 5, Up: Chapter 5
Defining New Functions
======================
Normally functions are defined using the DEFUN macro. The basic
skeleton of a DEFUN looks like this:
(defun name (parameter*)
"Optional documentation string."
body-form*)
Any symbol can be used as a function name. (1) Usually function
names contain only alphabetic characters and hyphens, but other
characters are allowed and are used in certain naming conventions. For
instance, functions that convert one kind of value to another sometimes
use -> in the name. For example, a function to convert strings to
widgets might be called string->widget. The most important naming
convention is the one mentioned in Chapter 2, which is that you
construct compound names with hyphens rather than underscores or inner
caps. Thus, frob-widget is better Lisp style than either frob_widget or
frobWidget.
A function's parameter list defines the variables that will be used
to hold the arguments passed to the function when it's called. (2) If
the function takes no arguments, the list is empty, written as ().
Different flavors of parameters handle required, optional, multiple, and
keyword arguments. I'll discuss the details in the next section.
If a string literal follows the parameter list, it's a documentation
string that should describe the purpose of the function. When the
function is defined, the documentation string will be associated with
the name of the function and can later be obtained using the
DOCUMENTATION function. (3)
Finally, the body of a DEFUN consists of any number of Lisp
expressions. They will be evaluated in order when the function is
called and the value of the last expression is returned as the value of
the function. Or the RETURN-FROM special operator can be used to return
immediately from anywhere in a function, as I'll discuss in a moment.
In Chapter 2 we wrote a hello-world function, which looked like this:
(defun hello-world () (format t "hello, world"))
You can now analyze the parts of this function. Its name is
hello-world, its parameter list is empty so it takes no arguments, it
has no documentation string, and its body consists of one expression.
(format t "hello, world")
The following is a slightly more complex function:
(defun verbose-sum (x y)
"Sum any two numbers after printing a message."
(format t "Summing ~d and ~d.~%" x y)
(+ x y))
This function is named verbose-sum, takes two arguments that will be
bound to the parameters x and y, has a documentation string, and has a
body consisting of two expressions. The value returned by the call to +
becomes the return value of verbose-sum.
---------- Footnotes ----------
(1) Well, almost any symbol. It's undefined what happens if you use
any of the names defined in the language standard as a name for one of
your own functions. However, as you'll see in Chapter 21, the Lisp
package system allows you to create names in different namespaces, so
this isn't really an issue.
(2) Parameter lists are sometimes also called lambda lists because of
the historical relationship between Lisp's notion of functions and the
lambda calculus.
(3) For example, the following:
(documentation 'foo 'function)
returns the documentation string for the function foo. Note,
however, that documentation strings are intended for human consumption,
not programmatic access. A Lisp implementation isn't required to store
them and is allowed to discard them at any time, so portable programs
shouldn't depend on their presence. In some implementations an
implementation-defined variable needs to be set before it will store
documentation strings.
File: pcl.info, Node: 5-2, Next: 5-3, Prev: 5-1, Up: Chapter 5
Function Parameter Lists
========================
There's not a lot more to say about function names or documentation
strings, and it will take a good portion of the rest of this book to
describe all the things you can do in the body of a function, which
leaves us with the parameter list.
The basic purpose of a parameter list is, of course, to declare the
variables that will receive the arguments passed to the function. When
a parameter list is a simple list of variable names-as in
verbose-sum-the parameters are called required parameters. When a
function is called, it must be supplied with one argument for every
required parameter. Each parameter is bound to the corresponding
argument. If a function is called with too few or too many arguments,
Lisp will signal an error.
However, Common Lisp's parameter lists also give you more flexible
ways of mapping the arguments in a function call to the function's
parameters. In addition to required parameters, a function can have
optional parameters. Or a function can have a single parameter that's
bound to a list containing any extra arguments. And, finally, arguments
can be mapped to parameters using keywords rather than position. Thus,
Common Lisp's parameter lists provide a convenient solution to several
common coding problems.
File: pcl.info, Node: 5-3, Next: 5-4, Prev: 5-2, Up: Chapter 5
Optional Parameters
===================
While many functions, like verbose-sum, need only required parameters,
not all functions are quite so simple. Sometimes a function will have a
parameter that only certain callers will care about, perhaps because
there's a reasonable default value. An example is a function that
creates a data structure that can grow as needed. Since the data
structure can grow, it doesn't matter-from a correctness point of
view-what the initial size is. But callers who have a good idea how
many items they're going to put into the data structure may be able to
improve performance by specifying a specific initial size. Most
callers, though, would probably rather let the code that implements the
data structure pick a good general-purpose value. In Common Lisp you
can accommodate both kinds of callers by using an optional parameter;
callers who don't care will get a reasonable default, and other callers
can provide a specific value. (1)
To define a function with optional parameters, after the names of any
required parameters, place the symbol &optional followed by the names of
the optional parameters. A simple example looks like this:
(defun foo (a b &optional c d) (list a b c d))
When the function is called, arguments are first bound to the
required parameters. After all the required parameters have been given
values, if there are any arguments left, their values are assigned to
the optional parameters. If the arguments run out before the optional
parameters do, the remaining optional parameters are bound to the value
NIL. Thus, the function defined previously gives the following results:
(foo 1 2) ==> (1 2 NIL NIL)
(foo 1 2 3) ==> (1 2 3 NIL)
(foo 1 2 3 4) ==> (1 2 3 4)
Lisp will still check that an appropriate number of arguments are
passed to the function-in this case between two and four, inclusive-and
will signal an error if the function is called with too few or too many.
Of course, you'll often want a different default value than NIL. You
can specify the default value by replacing the parameter name with a
list containing a name and an expression. The expression will be
evaluated only if the caller doesn't pass enough arguments to provide a
value for the optional parameter. The common case is simply to provide
a value as the expression.
(defun foo (a &optional (b 10)) (list a b))
This function requires one argument that will be bound to the
parameter a. The second parameter, b, will take either the value of the
second argument, if there is one, or 10.
(foo 1 2) ==> (1 2)
(foo 1) ==> (1 10)
Sometimes, however, you may need more flexibility in choosing the
default value. You may want to compute a default value based on other
parameters. And you can-the default-value expression can refer to
parameters that occur earlier in the parameter list. If you were
writing a function that returned some sort of representation of a
rectangle and you wanted to make it especially convenient to make
squares, you might use an argument list like this:
(defun make-rectangle (width &optional (height width)) ...)
which would cause the height parameter to take the same value as the
width parameter unless explicitly specified.
Occasionally, it's useful to know whether the value of an optional
argument was supplied by the caller or is the default value. Rather
than writing code to check whether the value of the parameter is the
default (which doesn't work anyway, if the caller happens to explicitly
pass the default value), you can add another variable name to the
parameter specifier after the default-value expression. This variable
will be bound to true if the caller actually supplied an argument for
this parameter and NIL otherwise. By convention, these variables are
usually named the same as the actual parameter with a "-supplied-p" on
the end. For example:
(defun foo (a b &optional (c 3 c-supplied-p))
(list a b c c-supplied-p))
This gives results like this:
(foo 1 2) ==> (1 2 3 NIL)
(foo 1 2 3) ==> (1 2 3 T)
(foo 1 2 4) ==> (1 2 4 T)
---------- Footnotes ----------
(1) In languages that don't support optional parameters directly,
programmers typically find ways to simulate them. One technique is to
use distinguished "no-value" values that the caller can pass to indicate
they want the default value of a given parameter. In C, for example,
it's common to use NULL as such a distinguished value. However, such a
protocol between the function and its callers is ad hoc-in some
functions or for some arguments NULL may be the distinguished value
while in other functions or for other arguments the magic value may be
-1 or some #defined constant.
File: pcl.info, Node: 5-4, Next: 5-5, Prev: 5-3, Up: Chapter 5
Rest Parameters
===============
Optional parameters are just the thing when you have discrete parameters
for which the caller may or may not want to provide values. But some
functions need to take a variable number of arguments. Several of the
built-in functions you've seen already work this way. FORMAT has two
required arguments, the stream and the control string. But after that
it needs a variable number of arguments depending on how many values
need to be interpolated into the control string. The + function also
takes a variable number of arguments-there's no particular reason to
limit it to summing just two numbers; it will sum any number of values.
(It even works with zero arguments, returning 0, the identity under
addition.) The following are all legal calls of those two functions:
(format t "hello, world")
(format t "hello, ~a" name)
(format t "x: ~d y: ~d" x y)
(+)
(+ 1)
(+ 1 2)
(+ 1 2 3)
Obviously, you could write functions taking a variable number of
arguments by simply giving them a lot of optional parameters. But that
would be incredibly painful-just writing the parameter list would be bad
enough, and that doesn't get into dealing with all the parameters in the
body of the function. To do it properly, you'd have to have as many
optional parameters as the number of arguments that can legally be
passed in a function call. This number is implementation dependent but
guaranteed to be at least 50. And in current implementations it ranges
from 4,096 to 536,870,911. (1) Blech. That kind of mind-bending tedium
is definitely not The Lisp Way.
Instead, Lisp lets you include a catchall parameter after the symbol
&rest. If a function includes a &rest parameter, any arguments
remaining after values have been doled out to all the required and
optional parameters are gathered up into a list that becomes the value
of the &rest parameter. Thus, the parameter lists for FORMAT and +
probably look something like this:
(defun format (stream string &rest values) ...)
(defun + (&rest numbers) ...)
---------- Footnotes ----------
(1) The constant CALL-ARGUMENTS-LIMIT tells you the
implementation-specific value.
File: pcl.info, Node: 5-5, Next: 5-6, Prev: 5-4, Up: Chapter 5
Keyword Parameters
==================
Optional and rest parameters give you quite a bit of flexibility, but
neither is going to help you out much in the following situation:
Suppose you have a function that takes four optional parameters. Now
suppose that most of the places the function is called, the caller wants
to provide a value for only one of the four parameters and, further,
that the callers are evenly divided as to which parameter they will use.
The callers who want to provide a value for the first parameter are
fine-they just pass the one optional argument and leave off the rest.
But all the other callers have to pass some value for between one and
three arguments they don't care about. Isn't that exactly the problem
optional parameters were designed to solve?
Of course it is. The problem is that optional parameters are still
positional-if the caller wants to pass an explicit value for the fourth
optional parameter, it turns the first three optional parameters into
required parameters for that caller. Luckily, another parameter flavor,
keyword parameters, allow the caller to specify which values go with
which parameters.
To give a function keyword parameters, after any required, &optional,
and &rest parameters you include the symbol &key and then any number of
keyword parameter specifiers, which work like optional parameter
specifiers. Here's a function that has only keyword parameters:
(defun foo (&key a b c) (list a b c))
When this function is called, each keyword parameters is bound to the
value immediately following a keyword of the same name. Recall from
Chapter 4 that keywords are names that start with a colon and that
they're automatically defined as self-evaluating constants.
If a given keyword doesn't appear in the argument list, then the
corresponding parameter is assigned its default value, just like an
optional parameter. Because the keyword arguments are labeled, they can
be passed in any order as long as they follow any required arguments.
For instance, foo can be invoked as follows:
(foo) ==> (NIL NIL NIL)
(foo :a 1) ==> (1 NIL NIL)
(foo :b 1) ==> (NIL 1 NIL)
(foo :c 1) ==> (NIL NIL 1)
(foo :a 1 :c 3) ==> (1 NIL 3)
(foo :a 1 :b 2 :c 3) ==> (1 2 3)
(foo :a 1 :c 3 :b 2) ==> (1 2 3)
As with optional parameters, keyword parameters can provide a default
value form and the name of a supplied-p variable. In both keyword and
optional parameters, the default value form can refer to parameters that
appear earlier in the parameter list.
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
(list a b c b-supplied-p))
(foo :a 1) ==> (1 0 1 NIL)
(foo :b 1) ==> (0 1 1 T)
(foo :b 1 :c 4) ==> (0 1 4 T)
(foo :a 2 :b 1 :c 4) ==> (2 1 4 T)
Also, if for some reason you want the keyword the caller uses to
specify the parameter to be different from the name of the actual
parameter, you can replace the parameter name with another list
containing the keyword to use when calling the function and the name to
be used for the parameter. The following definition of foo:
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
(list a b c c-supplied-p))
lets the caller call it like this:
(foo :apple 10 :box 20 :charlie 30) ==> (10 20 30 T)
This style is mostly useful if you want to completely decouple the
public API of the function from the internal details, usually because
you want to use short variable names internally but descriptive keywords
in the API. It's not, however, very frequently used.
File: pcl.info, Node: 5-6, Next: 5-7, Prev: 5-5, Up: Chapter 5
Mixing Different Parameter Types
================================
It's possible, but rare, to use all four flavors of parameters in a
single function. Whenever more than one flavor of parameter is used,
they must be declared in the order I've discussed them: first the names
of the required parameters, then the optional parameters, then the rest
parameter, and finally the keyword parameters. Typically, however, in
functions that use multiple flavors of parameters, you'll combine
required parameters with one other flavor or possibly combine &optional
and &rest parameters. The other two combinations, either &optional or
&rest parameters combined with &key parameters, can lead to somewhat
surprising behavior.
Combining &optional and &key parameters yields surprising enough
results that you should probably avoid it altogether. The problem is
that if a caller doesn't supply values for all the optional parameters,
then those parameters will eat up the keywords and values intended for
the keyword parameters. For instance, this function unwisely mixes
&optional and &key parameters:
(defun foo (x &optional y &key z) (list x y z))
If called like this, it works fine:
(foo 1 2 :z 3) ==> (1 2 3)
And this is also fine:
(foo 1) ==> (1 nil nil)
But this will signal an error:
(foo 1 :z 3) ==> ERROR
This is because the keyword :z is taken as a value to fill the
optional y parameter, leaving only the argument 3 to be processed. At
that point, Lisp will be expecting either a keyword/value pair or
nothing and will complain. Perhaps even worse, if the function had had
two &optional parameters, this last call would have resulted in the
values :z and 3 being bound to the two &optional parameters and the &key
parameter z getting the default value NIL with no indication that
anything was amiss.
In general, if you find yourself writing a function that uses both
&optional and &key parameters, you should probably just change it to use
all &key parameters-they're more flexible, and you can always add new
keyword parameters without disturbing existing callers of the function.
You can also remove keyword parameters, as long as no one is using them.
(1) In general, using keyword parameters helps make code much easier to
maintain and evolve-if you need to add some new behavior to a function
that requires new parameters, you can add keyword parameters without
having to touch, or even recompile, any existing code that calls the
function.
You can safely combine &rest and &key parameters, but the behavior
may be a bit surprising initially. Normally the presence of either
&rest or &key in a parameter list causes all the values remaining after
the required and &optional parameters have been filled in to be
processed in a particular way-either gathered into a list for a &rest
parameter or assigned to the appropriate &key parameters based on the
keywords. If both &rest and &key appear in a parameter list, then both
things happen-all the remaining values, which include the keywords
themselves, are gathered into a list that's bound to the &rest
parameter, and the appropriate values are also bound to the &key
parameters. So, given this function:
(defun foo (&rest rest &key a b c) (list rest a b c))
you get this result:
(foo :a 1 :b 2 :c 3) ==> ((:A 1 :B 2 :C 3) 1 2 3)
---------- Footnotes ----------
(1) Four standard functions take both &optional and &key
arguments-READ-FROM-STRING, PARSE-NAMESTRING, WRITE-LINE, and
WRITE-STRING. They were left that way during standardization for
backward compatibility with earlier Lisp dialects. READ-FROM-STRING
tends to be the one that catches new Lisp programmers most frequently-a
call such as (read-from-string s :start 10) seems to ignore the :start
keyword argument, reading from index 0 instead of 10. That's because
READ-FROM-STRING also has two &optional parameters that swallowed up the
arguments :start and 10.
File: pcl.info, Node: 5-7, Next: 5-8, Prev: 5-6, Up: Chapter 5
Function Return Values
======================
All the functions you've written so far have used the default behavior
of returning the value of the last expression evaluated as their own
return value. This is the most common way to return a value from a
function.
However, sometimes it's convenient to be able to return from the
middle of a function such as when you want to break out of nested
control constructs. In such cases you can use the RETURN-FROM special
operator to immediately return any value from the function.
You'll see in Chapter 20 that RETURN-FROM is actually not tied to
functions at all; it's used to return from a block of code defined with
the BLOCK special operator. However, DEFUN automatically wraps the
whole function body in a block with the same name as the function. So,
evaluating a RETURN-FROM with the name of the function and the value you
want to return will cause the function to immediately exit with that
value. RETURN-FROM is a special operator whose first "argument" is the
name of the block from which to return. This name isn't evaluated and
thus isn't quoted.
The following function uses nested loops to find the first pair of
numbers, each less than 10, whose product is greater than the argument,
and it uses RETURN-FROM to return the pair as soon as it finds it:
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
Admittedly, having to specify the name of the function you're
returning from is a bit of a pain-for one thing, if you change the
function's name, you'll need to change the name used in the RETURN-FROM
as well. (1) But it's also the case that explicit RETURN-FROMs are used
much less frequently in Lisp than return statements in C-derived
languages, because all Lisp expressions, including control constructs
such as loops and conditionals, evaluate to a value. So it's not much
of a problem in practice.
---------- Footnotes ----------
(1) Another macro, RETURN, doesn't require a name. However, you
can't use it instead of RETURN-FROM to avoid having to specify the
function name; it's syntactic sugar for returning from a block named
NIL. I'll cover it, along with the details of BLOCK and RETURN-FROM, in
Chapter 20.
File: pcl.info, Node: 5-8, Next: 5-9, Prev: 5-7, Up: Chapter 5
Functions As Data, a.k.a. Higher-Order Functions
================================================
While the main way you use functions is to call them by name, a number
of situations exist where it's useful to be able treat functions as
data. For instance, if you can pass one function as an argument to
another, you can write a general-purpose sorting function while allowing
the caller to provide a function that's responsible for comparing any
two elements. Then the same underlying algorithm can be used with many
different comparison functions. Similarly, callbacks and hooks depend
on being able to store references to code in order to run it later.
Since functions are already the standard way to abstract bits of code,
it makes sense to allow functions to be treated as data. (1)
In Lisp, functions are just another kind of object. When you define
a function with DEFUN, you're really doing two things: creating a new
function object and giving it a name. It's also possible, as you saw in
Chapter 3, to use LAMBDA expressions to create a function without giving
it a name. The actual representation of a function object, whether
named or anonymous, is opaque-in a native-compiling Lisp, it probably
consists mostly of machine code. The only things you need to know are
how to get hold of it and how to invoke it once you've got it.
The special operator FUNCTION provides the mechanism for getting at a
function object. It takes a single argument and returns the function
with that name. The name isn't quoted. Thus, if you've defined a
function foo, like so:
CL-USER> (defun foo (x) (* 2 x))
FOO
you can get the function object like this: (2)
CL-USER> (function foo)
#<Interpreted Function FOO>
In fact, you've already used FUNCTION, but it was in disguise. The
syntax #', which you used in Chapter 3, is syntactic sugar for FUNCTION,
just the way ' is syntactic sugar for QUOTE. (3) Thus, you can also get
the function object for foo like this:
CL-USER> #'foo
#<Interpreted Function FOO>
Once you've got the function object, there's really only one thing
you can do with it-invoke it. Common Lisp provides two functions for
invoking a function through a function object: FUNCALL and APPLY. (4)
They differ only in how they obtain the arguments to pass to the
function.
FUNCALL is the one to use when you know the number of arguments
you're going to pass to the function at the time you write the code.
The first argument to FUNCALL is the function object to be invoked, and
the rest of the arguments are passed onto that function. Thus, the
following two expressions are equivalent:
(foo 1 2 3) === (funcall #'foo 1 2 3)
However, there's little point in using FUNCALL to call a function
whose name you know when you write the code. In fact, the previous two
expressions will quite likely compile to exactly the same machine
instructions.
The following function demonstrates a more apt use of FUNCALL. It
accepts a function object as an argument and plots a simple ASCII-art
histogram of the values returned by the argument function when it's
invoked on the values from min to max, stepping by step.
(defun plot (fn min max step)
(loop for i from min to max by step do
(loop repeat (funcall fn i) do (format t "*"))
(format t "~%")))
The FUNCALL expression computes the value of the function for each
value of i. The inner LOOP uses that computed value to determine how
many times to print an asterisk to standard output.
Note that you don't use FUNCTION or #' to get the function value of
fn; you want it to be interpreted as a variable because it's the
variable's value that will be the function object. You can call plot
with any function that takes a single numeric argument, such as the
built-in function EXP that returns the value of e raised to the power of
its argument.
CL-USER> (plot #'exp 0 4 1/2)
*
*
**
****
*******
************
********************
*********************************
******************************************************
NIL
FUNCALL, however, doesn't do you any good when the argument list is
known only at runtime. For instance, to stick with the plot function
for another moment, suppose you've obtained a list containing a function
object, a minimum and maximum value, and a step value. In other words,
the list contains the values you want to pass as arguments to plot.
Suppose this list is in the variable plot-data. You could invoke plot
on the values in that list like this:
(plot (first plot-data) (second plot-data) (third plot-data) (fourth plot-data))
This works fine, but it's pretty annoying to have to explicitly
unpack the arguments just so you can pass them to plot.
That's where APPLY comes in. Like FUNCALL, the first argument to
APPLY is a function object. But after the function object, instead of
individual arguments, it expects a list. It then applies the function
to the values in the list. This allows you to write the following
instead:
(apply #'plot plot-data)
As a further convenience, APPLY can also accept "loose" arguments as
long as the last argument is a list. Thus, if plot-data contained just
the min, max, and step values, you could still use APPLY like this to
plot the EXP function over that range:
(apply #'plot #'exp plot-data)
APPLY doesn't care about whether the function being applied takes
&optional, &rest, or &key arguments-the argument list produced by
combining any loose arguments with the final list must be a legal
argument list for the function with enough arguments for all the
required parameters and only appropriate keyword parameters.
---------- Footnotes ----------
(1) Lisp, of course, isn't the only language to treat functions as
data. C uses function pointers, Perl uses subroutine references, Python
uses a scheme similar to Lisp, and C# introduces delegates, essentially
typed function pointers, as an improvement over Java's rather clunky
reflection and anonymous class mechanisms.
(2) The exact printed representation of a function object will differ
from implementation to implementation.
(3) The best way to think of FUNCTION is as a special kind of
quotation. QUOTEing a symbol prevents it from being evaluated at all,
resulting in the symbol itself rather than the value of the variable
named by that symbol. FUNCTION also circumvents the normal evaluation
rule but, instead of preventing the symbol from being evaluated at all,
causes it to be evaluated as the name of a function, just the way it
would if it were used as the function name in a function call
expression.
(4) There's actually a third, the special operator
MULTIPLE-VALUE-CALL, but I'll save that for when I discuss expressions
that return multiple values in Chapter 20.
File: pcl.info, Node: 5-9, Next: Chapter 6, Prev: 5-7, Up: Chapter 5
Anonymous Functions
===================
Once you start writing, or even simply using, functions that accept
other functions as arguments, you're bound to discover that sometimes
it's annoying to have to define and name a whole separate function
that's used in only one place, especially when you never call it by
name.
When it seems like overkill to define a new function with DEFUN, you
can create an "anonymous" function using a LAMBDA expression. As
discussed in Chapter 3, a LAMBDA expression looks like this:
(lambda (parameters) body)
One way to think of LAMBDA expressions is as a special kind of
function name where the name itself directly describes what the function
does. This explains why you can use a LAMBDA expression in the place of
a function name with #'.
(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5
You can even use a LAMBDA expression as the "name" of a function in a
function call expression. If you wanted, you could write the previous
FUNCALL expression more concisely.
((lambda (x y) (+ x y)) 2 3) ==> 5
But this is almost never done; it's merely worth noting that it's
legal in order to emphasize that LAMBDA expressions can be used anywhere
a normal function name can be. (1)
Anonymous functions can be useful when you need to pass a function as
an argument to another function and the function you need to pass is
simple enough to express inline. For instance, suppose you wanted to
plot the function 2x. You could define the following function:
(defun double (x) (* 2 x))
which you could then pass to plot.
CL-USER> (plot #'double 0 10 1)
**
****
******
********
**********
************
**************
****************
******************
********************
NIL
But it's easier, and arguably clearer, to write this:
CL-USER> (plot #'(lambda (x) (* 2 x)) 0 10 1)
**
****
******
********
**********
************
**************
****************
******************
********************
NIL
The other important use of LAMBDA expressions is in making closures,
functions that capture part of the environment where they're created.
You used closures a bit in Chapter 3, but the details of how closures
work and what they're used for is really more about how variables work
than functions, so I'll save that discussion for the next chapter.
---------- Footnotes ----------
(1) In Common Lisp it's also possible to use a LAMBDA expression as
an argument to FUNCALL (or some other function that takes a function
argument such as SORT or MAPCAR) with no #' before it, like this:
(funcall (lambda (x y) (+ x y)) 2 3)
This is legal and is equivalent to the version with the #' but for a
tricky reason. Historically LAMBDA expressions by themselves weren't
expressions that could be evaluated. That is LAMBDA wasn't the name of
a function, macro, or special operator. Rather, a list starting with
the symbol LAMBDA was a special syntactic construct that Lisp recognized
as a kind of function name.
But if that were still true, then (funcall (lambda (...) ...))
would be illegal because FUNCALL is a function and the normal evaluation
rule for a function call would require that the LAMBDA expression be
evaluated. However, late in the ANSI standardization process, in order
to make it possible to implement ISLISP, another Lisp dialect being
standardized at the same time, strictly as a user-level compatibility
layer on top of Common Lisp, a LAMBDA macro was defined that expands
into a call to FUNCTION wrapped around the LAMBDA expression. In other
words, the following LAMBDA expression:
(lambda () 42)
expands into the following when it occurs in a context where it
evaluated:
(function (lambda () 42)) ; or #'(lambda () 42)
This makes its use in a value position, such as an argument to
FUNCALL, legal. In other words, it's pure syntactic sugar. Most folks
either always use #' before LAMBDA expressions in value positions or
never do. In this book, I always use #'.
File: pcl.info, Node: Chapter 6, Next: Chapter 7, Prev: Chapter 5, Up: Top
6. Variables
============
The next basic building block we need to look at are variables. Common
Lisp supports two kinds of variables: lexical and dynamic. (1) These
two types correspond roughly to "local" and "global" variables in other
languages. However, the correspondence is only approximate. On one
hand, some languages' "local" variables are in fact much like Common
Lisp's dynamic variables. (2) And on the other, some languages' local
variables are lexically scoped without providing all the capabilities
provided by Common Lisp's lexical variables. In particular, not all
languages that provide lexically scoped variables support closures.
To make matters a bit more confusing, many of the forms that deal
with variables can be used with both lexical and dynamic variables. So
I'll start by discussing a few aspects of Lisp's variables that apply to
both kinds and then cover the specific characteristics of lexical and
dynamic variables. Then I'll discuss Common Lisp's general-purpose
assignment operator, SETF, which is used to assign new values to
variables and just about every other place that can hold a value.
* Menu:
* 6-1:: Variable Basics
* 6-2:: Lexical Variables and Closures
* 6-3:: Dynamic, a.k.a. Special, Variables
* 6-4:: Constants
* 6-5:: Assignment
* 6-6:: Generalized Assignment
* 6-7:: Other Ways to Modify Places
---------- Footnotes ----------
(1) Dynamic variables are also sometimes called special variables for
reasons you'll see later in this chapter. It's important to be aware of
this synonym, as some folks (and Lisp implementations) use one term
while others use the other.
(2) Early Lisps tended to use dynamic variables for local variables,
at least when interpreted. Elisp, the Lisp dialect used in Emacs, is a
bit of a throwback in this respect, continuing to support only dynamic
variables. Other languages have recapitulated this transition from
dynamic to lexical variables-Perl's local variables, for instance, are
dynamic while its my variables, introduced in Perl 5, are lexical.
Python never had true dynamic variables but only introduced true lexical
scoping in version 2.2. (Python's lexical variables are still somewhat
limited compared to Lisp's because of the conflation of assignment and
binding in the language's syntax.)
File: pcl.info, Node: 6-1, Next: 6-2, Prev: Chapter 6, Up: Chapter 6
Variable Basics
===============
As in other languages, in Common Lisp variables are named places that
can hold a value. However, in Common Lisp, variables aren't typed the
way they are in languages such as Java or C++. That is, you don't need
to declare the type of object that each variable can hold. Instead, a
variable can hold values of any type and the values carry type
information that can be used to check types at runtime. Thus, Common
Lisp is dynamically typed-type errors are detected dynamically. For
instance, if you pass something other than a number to the + function,
Common Lisp will signal a type error. On the other hand, Common Lisp is
a strongly typed language in the sense that all type errors will be
detected-there's no way to treat an object as an instance of a class
that it's not. (1)
All values in Common Lisp are, conceptually at least, references to
objects. (2) Consequently, assigning a variable a new value changes
what object the variable refers to but has no effect on the previously
referenced object. However, if a variable holds a reference to a
mutable object, you can use that reference to modify the object, and the
modification will be visible to any code that has a reference to the
same object.
One way to introduce new variables you've already used is to define
function parameters. As you saw in the previous chapter, when you
define a function with DEFUN, the parameter list defines the variables
that will hold the function's arguments when it's called. For example,
this function defines three variables-x, y, and z-to hold its arguments.
(defun foo (x y z) (+ x y z))
Each time a function is called, Lisp creates new bindings to hold the
arguments passed by the function's caller. A binding is the runtime
manifestation of a variable. A single variable-the thing you can point
to in the program's source code-can have many different bindings during
a run of the program. A single variable can even have multiple bindings
at the same time; parameters to a recursive function, for example, are
rebound for each call to the function.
As with all Common Lisp variables, function parameters hold object
references. (3) Thus, you can assign a new value to a function
parameter within the body of the function, and it will not affect the
bindings created for another call to the same function. But if the
object passed to a function is mutable and you change it in the
function, the changes will be visible to the caller since both the
caller and the callee will be referencing the same object.
Another form that introduces new variables is the LET special
operator. The skeleton of a LET form looks like this:
(let (variable*)
body-form*)
where each variable is a variable initialization form. Each
initialization form is either a list containing a variable name and an
initial value form or-as a shorthand for initializing the variable to
NIL-a plain variable name. The following LET form, for example, binds
the three variables x, y, and z with initial values 10, 20, and NIL:
(let ((x 10) (y 20) z)
...)
When the LET form is evaluated, all the initial value forms are first
evaluated. Then new bindings are created and initialized to the
appropriate initial values before the body forms are executed. Within
the body of the LET, the variable names refer to the newly created
bindings. After the LET, the names refer to whatever, if anything, they
referred to before the LET.
The value of the last expression in the body is returned as the value
of the LET expression. Like function parameters, variables introduced
with LET are rebound each time the LET is entered. (4)
The scope of function parameters and LET variables-the area of the
program where the variable name can be used to refer to the variable's
binding-is delimited by the form that introduces the variable. This
form-the function definition or the LET-is called the binding form. As
you'll see in a bit, the two types of variables-lexical and dynamic-use
two slightly different scoping mechanisms, but in both cases the scope
is delimited by the binding form.
If you nest binding forms that introduce variables with the same
name, then the bindings of the innermost variable shadows the outer
bindings. For instance, when the following function is called, a
binding is created for the parameter x to hold the function's argument.
Then the first LET creates a new binding with the initial value 2, and
the inner LET creates yet another binding, this one with the initial
value 3. The bars on the right mark the scope of each binding.
(defun foo (x)
(format t "Parameter: ~a~%" x) ; |<------ x is argument
(let ((x 2)) ; |
(format t "Outer LET: ~a~%" x) ; | |<---- x is 2
(let ((x 3)) ; | |
(format t "Inner LET: ~a~%" x)) ; | | |<-- x is 3
(format t "Outer LET: ~a~%" x)) ; | |
(format t "Parameter: ~a~%" x)) ; |
Each reference to x will refer to the binding with the smallest
enclosing scope. Once control leaves the scope of one binding form, the
binding from the immediately enclosing scope is unshadowed and x refers
to it instead. Thus, calling foo results in this output:
CL-USER> (foo 1)
Parameter: 1
Outer LET: 2
Inner LET: 3
Outer LET: 2
Parameter: 1
NIL
In future chapters I'll discuss other constructs that also serve as
binding forms-any construct that introduces a new variable name that's
usable only within the construct is a binding form.
For instance, in Chapter 7 you'll meet the DOTIMES loop, a basic
counting loop. It introduces a variable that holds the value of a
counter that's incremented each time through the loop. The following
loop, for example, which prints the numbers from 0 to 9, binds the
variable x:
(dotimes (x 10) (format t "~d " x))
Another binding form is a variant of LET, LET*. The difference is
that in a LET, the variable names can be used only in the body of the
LET-the part of the LET after the variables list-but in a LET*, the
initial value forms for each variable can refer to variables introduced
earlier in the variables list. Thus, you can write the following:
(let* ((x 10)
(y (+ x 10)))
(list x y))
but not this:
(let ((x 10)
(y (+ x 10)))
(list x y))
However, you could achieve the same result with nested LETs.
(let ((x 10))
(let ((y (+ x 10)))
(list x y)))
---------- Footnotes ----------
(1) Actually, it's not quite true to say that all type errors will
always be detected-it's possible to use optional declarations to tell
the compiler that certain variables will always contain objects of a
particular type and to turn off runtime type checking in certain regions
of code. However, declarations of this sort are used to optimize code
after it has been developed and debugged, not during normal development.
(2) As an optimization certain kinds of objects, such as integers
below a certain size and characters, may be represented directly in
memory where other objects would be represented by a pointer to the
actual object. However, since integers and characters are immutable, it
doesn't matter that there may be multiple copies of "the same" object in
different variables. This is the root of the difference between EQ and
EQL discussed in Chapter 4.
(3) In compiler-writer terms Common Lisp functions are
"pass-by-value." However, the values that are passed are references to
objects. This is similar to how Java and Python work.
(4) The variables in LET forms and function parameters are created by
exactly the same mechanism. In fact, in some Lisp dialects-though not
Common Lisp-LET is simply a macro that expands into a call to an
anonymous function. That is, in those dialects, the following:
(let ((x 10)) (format t "~a" x))
is a macro form that expands into this:
((lambda (x) (format t "~a" x)) 10)
File: pcl.info, Node: 6-2, Next: 6-3, Prev: 6-1, Up: Chapter 6
Lexical Variables and Closures
==============================
By default all binding forms in Common Lisp introduce lexically scoped
variables. Lexically scoped variables can be referred to only by code
that's textually within the binding form. Lexical scoping should be
familiar to anyone who has programmed in Java, C, Perl, or Python since
they all provide lexically scoped "local" variables. For that matter,
Algol programmers should also feel right at home, as Algol first
introduced lexical scoping in the 1960s.
However, Common Lisp's lexical variables are lexical variables with a
twist, at least compared to the original Algol model. The twist is
provided by the combination of lexical scoping with nested functions.
By the rules of lexical scoping, only code textually within the binding
form can refer to a lexical variable. But what happens when an
anonymous function contains a reference to a lexical variable from an
enclosing scope? For instance, in this expression:
(let ((count 0)) #'(lambda () (setf count (1+ count))))
the reference to count inside the LAMBDA form should be legal
according to the rules of lexical scoping. Yet the anonymous function
containing the reference will be returned as the value of the LET form
and can be invoked, via FUNCALL, by code that's not in the scope of the
LET. So what happens? As it turns out, when count is a lexical
variable, it just works. The binding of count created when the flow of
control entered the LET form will stick around for as long as needed, in
this case for as long as someone holds onto a reference to the function
object returned by the LET form. The anonymous function is called a
closure because it "closes over" the binding created by the LET.
The key thing to understand about closures is that it's the binding,
not the value of the variable, that's captured. Thus, a closure can not
only access the value of the variables it closes over but can also
assign new values that will persist between calls to the closure. For
instance, you can capture the closure created by the previous expression
in a global variable like this:
(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))
Then each time you invoke it, the value of count will increase by
one.
CL-USER> (funcall *fn*)
1
CL-USER> (funcall *fn*)
2
CL-USER> (funcall *fn*)
3
A single closure can close over many variable bindings simply by
referring to them. Or multiple closures can capture the same binding.
For instance, the following expression returns a list of three closures,
one that increments the value of the closed over count binding, one that
decrements it, and one that returns the current value:
(let ((count 0))
(list
#'(lambda () (incf count))
#'(lambda () (decf count))
#'(lambda () count)))
File: pcl.info, Node: 6-3, Next: 6-4, Prev: 6-2, Up: Chapter 6
Dynamic, a.k.a. Special, Variables
==================================
Lexically scoped bindings help keep code understandable by limiting the
scope, literally, in which a given name has meaning. This is why most
modern languages use lexical scoping for local variables. Sometimes,
however, you really want a global variable-a variable that you can refer
to from anywhere in your program. While it's true that indiscriminate
use of global variables can turn code into spaghetti nearly as quickly
as unrestrained use of goto, global variables do have legitimate uses
and exist in one form or another in almost every programming language.
(1) And as you'll see in a moment, Lisp's version of global variables,
dynamic variables, are both more useful and more manageable.
Common Lisp provides two ways to create global variables: DEFVAR and
DEFPARAMETER. Both forms take a variable name, an initial value, and an
optional documentation string. After it has been DEFVARed or
DEFPARAMETERed, the name can be used anywhere to refer to the current
binding of the global variable. As you've seen in previous chapters,
global variables are conventionally named with names that start and end
with *. You'll see later in this section why it's quite important to
follow that naming convention. Examples of DEFVAR and DEFPARAMETER look
like this:
(defvar *count* 0
"Count of widgets made so far.")
(defparameter *gap-tolerance* 0.001
"Tolerance to be allowed in widget gaps.")
The difference between the two forms is that DEFPARAMETER always
assigns the initial value to the named variable while DEFVAR does so
only if the variable is undefined. A DEFVAR form can also be used with
no initial value to define a global variable without giving it a value.
Such a variable is said to be unbound.
Practically speaking, you should use DEFVAR to define variables that
will contain data you'd want to keep even if you made a change to the
source code that uses the variable. For instance, suppose the two
variables defined previously are part of an application for controlling
a widget factory. It's appropriate to define the *count* variable with
DEFVAR because the number of widgets made so far isn't invalidated just
because you make some changes to the widget-making code. (2)
On the other hand, the variable *gap-tolerance* presumably has some
effect on the behavior of the widget-making code itself. If you decide
you need a tighter or looser tolerance and change the value in the
DEFPARAMETER form, you'd like the change to take effect when you
recompile and reload the file.
After defining a variable with DEFVAR or DEFPARAMETER, you can refer
to it from anywhere. For instance, you might define this function to
increment the count of widgets made:
(defun increment-widget-count () (incf *count*))
The advantage of global variables is that you don't have to pass them
around. Most languages store the standard input and output streams in
global variables for exactly this reason-you never know when you're
going to want to print something to standard out, and you don't want
every function to have to accept and pass on arguments containing those
streams just in case someone further down the line needs them.
However, once a value, such as the standard output stream, is stored
in a global variable and you have written code that references that
global variable, it's tempting to try to temporarily modify the behavior
of that code by changing the variable's value.
For instance, suppose you're working on a program that contains some
low-level logging functions that print to the stream in the global
variable *standard-output*. Now suppose that in part of the program you
want to capture all the output generated by those functions into a file.
You might open a file and assign the resulting stream to
*standard-output*. Now the low-level functions will send their output
to the file.
This works fine until you forget to set *standard-output* back to the
original stream when you're done. If you forget to reset
*standard-output*, all the other code in the program that uses
*standard-output* will also send its output to the file. (3)
What you really want, it seems, is a way to wrap a piece of code in
something that says, "All code below here-all the functions it calls,
all the functions they call, and so on, down to the lowest-level
functions-should use this value for the global variable
*standard-output*." Then when the high-level function returns, the old
value of *standard-output* should be automatically restored.
It turns out that that's exactly what Common Lisp's other kind of
variable-dynamic variables-let you do. When you bind a dynamic
variable-for example, with a LET variable or a function parameter-the
binding that's created on entry to the binding form replaces the global
binding for the duration of the binding form. Unlike a lexical binding,
which can be referenced by code only within the lexical scope of the
binding form, a dynamic binding can be referenced by any code that's
invoked during the execution of the binding form. (4) And it turns out
that all global variables are, in fact, dynamic variables.
Thus, if you want to temporarily redefine *standard-output*, the way
to do it is simply to rebind it, say, with a LET.
(let ((*standard-output* *some-other-stream*))
(stuff))
In any code that runs as a result of the call to stuff, references to
*standard-output* will use the binding established by the LET. And when
stuff returns and control leaves the LET, the new binding of
*standard-output* will go away and subsequent references to
*standard-output* will see the binding that was current before the LET.
At any given time, the most recently established binding shadows all
other bindings. Conceptually, each new binding for a given dynamic
variable is pushed onto a stack of bindings for that variable, and
references to the variable always use the most recent binding. As
binding forms return, the bindings they created are popped off the
stack, exposing previous bindings. (5)
A simple example shows how this works.
(defvar *x* 10)
(defun foo () (format t "X: ~d~%" *x*))
The DEFVAR creates a global binding for the variable *x* with the
value 10. The reference to *x* in foo will look up the current binding
dynamically. If you call foo from the top level, the global binding
created by the DEFVAR is the only binding available, so it prints 10.
CL-USER> (foo)
X: 10
NIL
But you can use LET to create a new binding that temporarily shadows
the global binding, and foo will print a different value.
CL-USER> (let ((*x* 20)) (foo))
X: 20
NIL
Now call foo again, with no LET, and it again sees the global
binding.
CL-USER> (foo)
X: 10
NIL
Now define another function.
(defun bar ()
(foo)
(let ((*x* 20)) (foo))
(foo))
Note that the middle call to foo is wrapped in a LET that binds *x*
to the new value 20. When you run bar, you get this result:
CL-USER> (bar)
X: 10
X: 20
X: 10
NIL
As you can see, the first call to foo sees the global binding, with
its value of 10. The middle call, however, sees the new binding, with
the value 20. But after the LET, foo once again sees the global
binding.
As with lexical bindings, assigning a new value affects only the
current binding. To see this, you can redefine foo to include an
assignment to *x*.
(defun foo ()
(format t "Before assignment~18tX: ~d~%" *x*)
(setf *x* (+ 1 *x*))
(format t "After assignment~18tX: ~d~%" *x*))
Now foo prints the value of *x*, increments it, and prints it again.
If you just run foo, you'll see this:
CL-USER> (foo)
Before assignment X: 10
After assignment X: 11
NIL
Not too surprising. Now run bar.
CL-USER> (bar)
Before assignment X: 11
After assignment X: 12
Before assignment X: 20
After assignment X: 21
Before assignment X: 12
After assignment X: 13
NIL
Notice that *x* started at 11-the earlier call to foo really did
change the global value. The first call to foo from bar increments the
global binding to 12. The middle call doesn't see the global binding
because of the LET. Then the last call can see the global binding again
and increments it from 12 to 13.
So how does this work? How does LET know that when it binds *x* it's
supposed to create a dynamic binding rather than a normal lexical
binding? It knows because the name has been declared special. (6) The
name of every variable defined with DEFVAR and DEFPARAMETER is
automatically declared globally special. This means whenever you use
such a name in a binding form-in a LET or as a function parameter or any
other construct that creates a new variable binding-the binding that's
created will be a dynamic binding. This is why the *naming*
*convention* is so important-it'd be bad news if you used a name for
what you thought was a lexical variable and that variable happened to be
globally special. On the one hand, code you call could change the value
of the binding out from under you; on the other, you might be shadowing
a binding established by code higher up on the stack. If you always
name global variables according to the * naming convention, you'll never
accidentally use a dynamic binding where you intend to establish a
lexical binding.
It's also possible to declare a name locally special. If, in a
binding form, you declare a name special, then the binding created for
that variable will be dynamic rather than lexical. Other code can
locally declare a name special in order to refer to the dynamic binding.
However, locally special variables are relatively rare, so you needn't
worry about them. (7)
Dynamic bindings make global variables much more manageable, but it's
important to notice they still allow action at a distance. Binding a
global variable has two at a distance effects-it can change the behavior
of downstream code, and it also opens the possibility that downstream
code will assign a new value to a binding established higher up on the
stack. You should use dynamic variables only when you need to take
advantage of one or both of these characteristics.
---------- Footnotes ----------
(1) Java disguises global variables as public static fields, C uses
extern variables, and Python's module-level and Perl's package-level
variables can likewise be accessed from anywhere.
(2) If you specifically want to reset a DEFVARed variable, you can
either set it directly with SETF or make it unbound using MAKUNBOUND and
then reevaluate the DEFVAR form.
(3) The strategy of temporarily reassigning *standard-output* also
breaks if the system is multithreaded-if there are multiple threads of
control trying to print to different streams at the same time, they'll
all try to set the global variable to the stream they want to use,
stomping all over each other. You could use a lock to control access to
the global variable, but then you're not really getting the benefit of
multiple concurrent threads, since whatever thread is printing has to
lock out all the other threads until it's done even if they want to
print to a different stream.
(4) The technical term for the interval during which references may
be made to a binding is its extent. Thus, scope and extent are
complementary notions-scope refers to space while extent refers to time.
Lexical variables have lexical scope but indefinite extent, meaning they
stick around for an indefinite interval, determined by how long they're
needed. Dynamic variables, by contrast, have indefinite scope since
they can be referred to from anywhere but dynamic extent. To further
confuse matters, the combination of indefinite scope and dynamic extent
is frequently referred to by the misnomer dynamic scope.
(5) Though the standard doesn't specify how to incorporate
multithreading into Common Lisp, implementations that provide
multithreading follow the practice established on the Lisp machines and
create dynamic bindings on a per-thread basis. A reference to a global
variable will find the binding most recently established in the current
thread, or the global binding.
(6) This is why dynamic variables are also sometimes called special
variables.
(7) If you must know, you can look up DECLARE, SPECIAL, and LOCALLY
in the HyperSpec.
File: pcl.info, Node: 6-4, Next: 6-5, Prev: 6-3, Up: Chapter 6
Constants
=========
One other kind of variable I haven't mentioned at all is the oxymoronic
"constant variable." All constants are global and are defined with
DEFCONSTANT. The basic form of DEFCONSTANT is like DEFPARAMETER.
(defconstant name initial-value-form [ documentation-string ])
As with DEFVAR and DEFPARAMETER, DEFCONSTANT has a global effect on
the name used-thereafter the name can be used only to refer to the
constant; it can't be used as a function parameter or rebound with any
other binding form. Thus, many Lisp programmers follow a naming
convention of using names starting and ending with + for constants.
This convention is somewhat less universally followed than the *-naming
convention for globally special names but is a good idea for the same
reason. (1)
Another thing to note about DEFCONSTANT is that while the language
allows you to redefine a constant by reevaluating a DEFCONSTANT with a
different initial-value-form, what exactly happens after the
redefinition isn't defined. In practice, most implementations will
require you to reevaluate any code that refers to the constant in order
to see the new value since the old value may well have been inlined.
Consequently, it's a good idea to use DEFCONSTANT only to define things
that are really constant, such as the value of NIL. For things you might
ever want to change, you should use DEFPARAMETER instead.
---------- Footnotes ----------
(1) Several key constants defined by the language itself don't follow
this convention-not least of which are T and NIL. This is occasionally
annoying when one wants to use t as a local variable name. Another is
PI, which holds the best long-float approximation of the mathematical
constant pi.
File: pcl.info, Node: 6-5, Next: 6-6, Prev: 6-4, Up: Chapter 6
Assignment
==========
Once you've created a binding, you can do two things with it: get the
current value and set it to a new value. As you saw in Chapter 4, a
symbol evaluates to the value of the variable it names, so you can get
the current value simply by referring to the variable. To assign a new
value to a binding, you use the SETF macro, Common Lisp's
general-purpose assignment operator. The basic form of SETF is as
follows:
(setf place value)
Because SETF is a macro, it can examine the form of the place it's
assigning to and expand into appropriate lower-level operations to
manipulate that place. When the place is a variable, it expands into a
call to the special operator SETQ, which, as a special operator, has
access to both lexical and dynamic bindings. (1) For instance, to
assign the value 10 to the variable x, you can write this:
(setf x 10)
As I discussed earlier, assigning a new value to a binding has no
effect on any other bindings of that variable. And it doesn't have any
effect on the value that was stored in the binding prior to the
assignment. Thus, the SETF in this function:
(defun foo (x) (setf x 10))
will have no effect on any value outside of foo. The binding that
was created when foo was called is set to 10, immediately replacing
whatever value was passed as an argument. In particular, a form such as
the following:
(let ((y 20))
(foo y)
(print y))
will print 20, not 10, as it's the value of y that's passed to foo
where it's briefly the value of the variable x before the SETF gives x a
new value.
SETF can also assign to multiple places in sequence. For instance,
instead of the following:
(setf x 1)
(setf y 2)
you can write this:
(setf x 1 y 2)
SETF returns the newly assigned value, so you can also nest calls to
SETF as in the following expression, which assigns both x and y the same
random value:
(setf x (setf y (random 10)))
---------- Footnotes ----------
(1) Some old-school Lispers prefer to use SETQ with variables, but
modern style tends to use SETF for all assignments.
File: pcl.info, Node: 6-6, Next: 6-7, Prev: 6-5, Up: Chapter 6
Generalized Assignment
======================
Variable bindings, of course, aren't the only places that can hold
values. Common Lisp supports composite data structures such as arrays,
hash tables, and lists, as well as user-defined data structures, all of
which consist of multiple places that can each hold a value.
I'll cover those data structures in future chapters, but while we're
on the topic of assignment, you should note that SETF can assign any
place a value. As I cover the different composite data structures, I'll
point out which functions can serve as "SETFable places." The short
version, however, is if you need to assign a value to a place, SETF is
almost certainly the tool to use. It's even possible to extend SETF to
allow it to assign to user-defined places though I won't cover that.
(1)
In this regard SETF is no different from the = assignment operator in
most C-derived languages. In those languages, the = operator assigns
new values to variables, array elements, and fields of classes. In
languages such as Perl and Python that support hash tables as a built-in
data type, = can also set the values of individual hash table entries.
Table 6-1 summarizes the various ways = is used in those languages.
Table 6-1. Assignment with = in Other Languages
|