Chapter 4. Making Decisions with Conditions

In the previous chapters, you learned some basic Lisp commands, as well as some of the philosophy behind Lisp. In this chapter, we’ll be looking in detail at commands for handling conditions. The elegance of these commands shows that the unusual philosophy and design of Lisp has real practical benefits.

The Symmetry of nil and ()

One thing is particularly striking when we look at how Lisp commands and data structures work: They are imbued with symmetry in every conceivable way. This symmetry can give your Lisp code a certain elegance that other languages cannot have, and Lisp’s simple syntax is an important factor in making this symmetry possible.

Empty Equals False

Since the Lisp philosophy strongly emphasizes the use of lists to store and manipulate information, it will come as no surprise that the design of Common Lisp favors behaviors that make it easy to slice and dice such lists. The most profound design decision made in Common Lisp, with regard to lists, is that it automatically treats an empty list as a false value when evaluating a condition:

> (if '()
      'i-am-true
      'i-am-false)

I-AM-FALSE

> (if '(1)
      'i-am-true
      'i-am-false)

I-AM-TRUE

This example shows that when we pass the empty list () into an if form, it evaluates as a false value, whereas a list that contains an item evaluates as true.

Because we can easily detect an empty list, we can process lists using recursion. With this technique, we can take items from the front of a list and send the rest of the list back to the same function until the list is empty. (It’s a good thing that detecting empty lists is so easy, because so many functions in Lisp end up being list-eaters.)

image with no caption

Let’s look at a common list-eating function, which calculates the length of a list.

> (defun my-length (list)
     (if list
         (1+ (my-length (cdr list)))
         0))

> (my-length '(list with four symbols))

4

This function is written in classic Lisp style. It calls itself recursively as it chomps items off the front of the list. Calling yourself in this way is not only allowed in Lisp, but is often strongly encouraged. Lists in Lisp are recursive (conses of conses of conses . . .), so the act of consuming a list maps naturally onto functions that are recursive.

Note

Calling yourself recursively can sometimes make for slow code. In Chapter 14, we’ll rewrite the my-length function using a special, potentially faster, type of recursion.

The Four Disguises of ()

Not only does the empty list evaluate to false, but it is the only false value in Common Lisp. Any value not equivalent to an empty list will be considered a true value. This explains why the expression '(1) in the earlier example was treated as true. However, there are some other expressions in Lisp that are disguises for the one and only empty list:

image with no caption

We can see that the expressions in this table are equivalent by comparing them with one another:

 (eq '() nil)  ==> T
 (eq '() ())   ==> T
 (eq '() 'nil) ==> T

Notice that the only value in the table that seems normal is the quoted list on the left side of the comparisons. The other three all seem to break the rules of Lisp forms that we talked about in the previous chapter.

The first two examples are particularly puzzling. They are missing the quotation mark that tells the Lisp environment, “Hey, this item is a piece of data, not code!” In the case of nil, you would expect that this would actually be the name of a variable that could have some kind of arbitrary value. In the case of the unquoted (), there’s no way you could tell what would happen. The parentheses look like a form of code that needs to be evaluated, but a Lisp form always has a symbol at the beginning, telling it what to do. What do we do when there’s nothing inside the form at all?

The bottom line is that Common Lisp is architected behind the scenes to make sure all four of these values look like an empty list when you use them in your program, allowing most Lisp conditionals to be written with an elegant brevity. For instance, there is a constant named nil that evaluates to itself and allows you to omit the quotation mark in the first case . The second case is a natural by-product of how Common Lisp parses an empty form. The third case is due to a requirement in the Common Lisp spec that says that () and nil should be treated the same.

Although there’s a certain beauty to having all of these values be the same, not every Lisper agrees with this sentiment. After all, are false and empty list really the same kind of thing? The creators of the other popular dialect of Lisp, Scheme, felt differently about this issue, and preferred to keep the concepts of falsity and empty list completely separate, at a slight cost to code brevity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.122.68