Subject: On conditionals From: Erik Naggum <erik@naggum.net> Date: Wed, 21 Nov 2001 02:45:42 GMT Newsgroups: comp.lang.lisp Message-ID: <3215299538573186@naggum.net> Many other programming languages have statements and expressions, but Common Lisp has not made that distinction quite as explicit, although the value of a lot of forms are not usually used. Still, when we turn to the conditionals, it is somewhat different. Common Lisp the Language (CLtL) introduces the conditionals by talking about cond being the primary Lisp conditional, but if being like if-then-else in other languages, it would be covered first. However, there is a significant difference that has made me think that this is not accurate. Lisp's if returns a value. The ifs of other programming languages are generally _statements_ that do not return a value. If they have a conditional that can return a value, it is usually very different from the usual if statement for one significant reason: It has to have a value for both the consequent and the alternate, while the traditional if forms have an optional alternate. In Common Lisp, an else-less if form works because nil is useful value, but this is not a good thing. The if form has some properties of both statement and expression. One question is whether a form has an implicit progn. This is a clue that it is has statement nature. So if it does not have an implicit progn, but only one value-returning form, this is a clue that it is an expression. One question is whether a form should be used for its value or not. The forms when and unless should clearly not be used for their value. They also have implicit progns. There is no point in a progn unless you have other forms satisfying another clue to statement nature: their value(s) are discarded and ignored. The if form is both statement and expression, and it is neither, because it does not quite have either nature. As an expression, it is not a good idea to leave out the alternate, even if you want that value to be nil. As a statement, adding explicit progns can be somewhat annoying. The general conditional cond also has some of this dual nature. It has implicit progn, but returns values. Leaving out the final branch is a known source of annoying bugs, whether it be the unexamined fall-through case or an unexpected nil return value. Still, cond is the smartest of the conditional forms I have seen in any number of programming languages: a long chain of "else if" looks like whoever designed that excuse for a language forgot to design it for more than either-or style cases. Also, the verbosity of the conditionals in inferior languages in this style gets in the way of the task at hand, so cond is the definite winner. The specialized conditionals case (with ccase and ecase) and typecase (with ctypecase and etypecase) cover common comparison cases that are easy to optimize and whose similarities would be totally obscured by a cond. E.g.,, a test for (<= 0 x 99) may be significantly more expensive than a test for the type (integer 0 99). A case "dispatching" on a set of characters may be implemented as a very fast table lookup that would otherwise be hard to do as efficiently. The one-branch conditionals when and unless might seem to be redundant, but they communicate something that neither if nor cond can communicate: A promise that there will not be any other alternatives, or that if its condition is not met, nothing needs to be done. If it is merely an accident that there is one branch now, which might change in the future, do not use when or unless, because you are lying to yourself. Both when and unless communicate an expectation. I think when and unless should be used such that the condition is expected to be true, in which case when evaluates the body and unless does not. In other words, unless says that when the condition is false, that is an exceptional thing, but when says that when the condition is false, that is nothing special. That is, you should expect both (when (not/null ...)) and (unless (not/null ...)). There are no serious coding standards that apply to the rich set of Common Lisp conditionals. I would, however, appeal to those who seek to use them all to their fullest not use if as a statement, but always as an expression with both branches specified. Even if the alternate branch is nil, specify it explicitly. (This may be read to imply that I think it was a mistake to make the alternate branch optional, but it was probably made optional because if looks so much like programming languages that had a statement-only if.) While other programming languages may have an if statement, e.g., C, Common Lisp's if is much closer in meaning to C's conditional expression, ?:, which is usually not abused to chain a whole lot of them together as you lose track of which condition must hold to reach each expression. In that case, use cond or one of the case forms as an expression, meaning: without using the option to include statements with the implicit progn in each body form, and ensure that you specify the value when none of the conditions or cases are met, even if it is nil. For the conditional statement, I would likewise appeal to those who seek to use the most of te language to use when and unless when there is only one known branch, but not use them for their value. However, there may be an implicit alternate branch even when using when or unless: They may throw or return or signal an error. I favor using unless for this, as explained above about unless being somewhat exceptional in nature. When there are more than one branch in the conditional statement, use cond. In the statement capacity, I favor _not_ terminating it with a final alternate branch unless that branch includes some statements of its own, so as not to confuse it with an expression. A final alternate branch like (t nil) tells me the value of the whole cond is used for something and thus I need to look for the return value for each condition. To make this abundantly clear, it may be prudent in a complex form to use values forms explicitly, even if returning only a single value, but this, too, may decrease readability if it is not obviously seen as useful. Whareas most programming language have a single conditional statement and, if you are lucky, maybe a conditional expression, Common Lisp once again proves itself as the language that communicates with programmers and captures their intent through the choice of which of several forms to use. Using only one form for a slew of different purposes is the same kind of impoverished literary style that you find in bad authors, but if you come from a language that has only one conditional, embrace the set of options you have in this new language. It may even be true that in the natural language of the programmer, even be it English, the set of conditional expressions fit the single if-then-else form of programming languages (the writing styles of many programmers may indicate that they would appreciate a limited vocabulary so as not to feel alienated), but then using Common Lisp would be an opportunity to improve both styles. /// -- Norway is now run by a priest from the fundamentalist Christian People's Party, the fifth largest party representing one eighth of the electorate. -- Carrying a Swiss Army pocket knife in Oslo, Norway, is a criminal offense.