And More . . .

And there’s more—I haven’t mentioned the ~? directive, which can take snippets of control strings from the format arguments or the ~/ directive, which allows you to call an arbitrary function to handle the next format argument. And then there are all the directives for generating tabular and pretty-printed output. But the directives discussed in this chapter should be plenty for the time being.

In the next chapter, you’ll move onto Common Lisp’s condition system, the Common Lisp analog to other languages’ exception and error handling systems.


1Of course, most folks realize it’s not worth getting that worked up over anything in a programming language and use it or not without a lot of angst. On the other hand, it’s interesting that these two features are the two features in Common Lisp that implement what are essentially domain-specific languages using a syntax not based on s-expressions. The syntax of **FORMAT**‘s control strings is character based, while the extended **LOOP** macro can be understood only in terms of the grammar of the **LOOP** keywords. That one of the common knocks on both **FORMAT** and **LOOP** is that they “aren’t Lispy enough” is evidence that Lispers really do like the s-expression syntax.

2Readers interested in the pretty printer may want to read the paper “XP: A Common Lisp Pretty Printing System” by Richard Waters. It’s a description of the pretty printer that was eventually incorporated into Common Lisp. You can download it from ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-1102a.pdf.

3To slightly confuse matters, most other I/O functions also accept **T** and **NIL** as stream designators but with a different meaning: as a stream designator, **T** designates the bidirectional stream ***TERMINAL-IO***, while **NIL** designates ***STANDARD-OUTPUT*** as an output stream and ***STANDARD-INPUT*** as an input stream.

4This variant on the ~C directive makes more sense on platforms like the Lisp Machines where key press events were represented by Lisp characters.

5Technically, if the argument isn’t a real number, ~F is supposed to format it as if by the ~D directive, which in turn behaves like the ~A directive if the argument isn’t a number, but not all implementations get this right.

6Well, that’s what the language standard says. For some reason, perhaps rooted in a common ancestral code base, several Common Lisp implementations don’t implement this aspect of the ~F directive correctly.

7If you find “I saw zero elves” to be a bit clunky, you could use a slightly more elaborate format string that makes another use of ~:* like this:

  1. (format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 0) ==> "I saw no elves."
  2. (format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 1) ==> "I saw one elf."
  3. (format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 2) ==> "I saw two elves."

8This kind of problem can arise when trying to localize an application and translate human-readable messages into different languages. **FORMAT** can help with some of these problems but is by no means a full-blown localization system.