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.