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.[53]
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
.
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)