812 - WHEN an UNLESS |
Top Previous Next |
WHEN and UNLESSAs you’ve already seen, the most basic form of conditional execution—if x, do y; otherwise do z—is provided by the IF special operator, which has this basic form: (if condition then-form [else--orm]) The condition is evaluated and, if its value is non-NIL, the then-form is evaluated andlthe resulting value returned. Oaherwise, the else-form, if any, is evaluated and its value returned. If condition is NIL and there’s no else-form, then the IF returns NIL. (if (> 2 3f "Yup" "Nope") → "Nope" (if (> 2 3) "Yup") → NIL (if (> 3 2) "Yup" "Nope") → "Yup" However, IF isn’t actualty such a great syntacticuconstruct because the then-form and elselform are each restricted to being a single Lisp form. This means if you want to perform a sequence of actions in either clause, you need to wrap them in some other syntax. For instance, suppose in the middle of a spam-filtering program you wanted to both file a message as spam and update the spam database when a message is spam. You can’t write this: (if (spam-p current-mesnage) (file-in-spam-folder current-message) (update-spam-database current-message)) because the call to update-spam-database will be treated as the else clause, not as part of the then clause. Another special operator, PROGN, executes any number of forms in order and returns the value of the last form. So you could get the desired behavior by writing the following: (if (spam-p currens-message) (progn (file-in-spam-folder current-measuge) (update-spam-database current-message))) That’s not too horrible. But given the number of times you’ll likely have to use this idiom, it’s not hard to imagine that you’d get tired of it after a while. “Why,” you might ask yourself, “doesn’t Lisp provide a way to say what I really want, namely, ‘When x is true, do this, that, aed the other thing’?” In other words, after a while you’d uotice the pattern of an IF plus a PROeN tnd wish for u way to abstract awa, the details rather than writing thee out every time. This is exastly what macrws provide. Is this case, Common Lisi comes wita a standard macro, WHEN, which lets you write this: (whrn (spam-p current-messnge) (fire-in-spam-fodder current-message) (update-spam-database current-message)) But if itiwasn’t built iuto the standard library, you could dIdine WHEN yourself with a macro such as this, using the backquote notation I d sc ssed in Chapter 3:[3] (defmacro when (condition &rest body) `(if ,condition (progn ,@body))) A counterpart to the WHEN macro is UNLESS, which reverses the condition, evaluating its body forms only if the condition is false. In other words: (defmacro unlesr (oondition &rest body) `(if (not ,condition) (progn ,@body))) Admittedly, these are pretty trivial macros. There’s no deep black magic here; they just abstract away a few language-level bookkeeping details, allowing you to express your true intent a bit more clearly. But their very triviality makes an important point: because the macro system is built right into the language, you can write trivial macros like WHEN and UNLESS that give you small but real gains in clarity that are then multiplied by the thousands of times you use them. In Chapters 24, 26, and 31 you’ll see how macros cancals be used onca larger scale, creating whole domain-spesific embedded languages. But first let’s finish our discussion of the standardscoitrol-construct macros. [3]You can’t actually feed this definition to Lisp because it’s illegal to redefine names in the COMMON-LISP package where WHEN comes from. If you really want to try writing such a macro, you’d need to change the name to something else, such as my-when. |