From: Jeff Dalton
Subject: Re: style-guide
Date: 
Message-ID: <7376@skye.ed.ac.uk>
In article <······················@monsun.si.no> ········@monsun.si.no (Hallvard Tr{tteberg) writes:
>I'm writing a style-guide for a big Lisp project, and would like
>comments on my first draft and suggestion for the empty chapters.

Thanks for posting your style guide.  I think most of it is 
excellent, but I disagree with some things.

1. The general rule "use the most specific construct" is a useful
guideline, but it's presented as if it should override (almost) every
other consideration.  In addition, I think it's a mistake to expect
a single best answer in every case, and the rigid application of the
"most specific" rule looks like it has that end in mind.

The application of this rule that I find least reasonable is the one
that decides between MAPC and DOLIST.  I don't think it's right to say
to use MAPC instead of DOLIST unless you want to RETURN from the loop.
There's nothing wrong with using DOLIST w/o RETURN and nothing wrong
with using MAPC for some reason other than "not using RETURN".  Maybe
you want to bring out a similarity to some other code that uses a
different mapping function.  Or maybe you just happen to prefer
mapping functions.

It's also possible that using the most specific construct will result
in code that is cleverer but less clear.  

A better rule is this one (also from your guide):

   The general rule, when choosing technique, is to use idioms that
   are easy to spot.

(BTW, I see nothing wrong in using SETQ instead of SETF on variables.)

2. I think that variables introduced by DEFPARAMETER are at least as
close to DEFVAR variables as they are constants.  For one thing,
treating parameters as constants implies that they should never be
rebound.  But if they can "be changed (possibly at run-time)", it may
be better to change them by binding rather than assignment.  The
difference between a paramater and a (DEFVAR) variable is, I would
say, that it's the program that changes the value of a variable and
the user (or "logical user") who changes the value of a parameter.
This does not, to my mind, imply that parameters are closer to
constants than to variables.

3. I think the problem of spotting dependencies in LET* is often
overemphasized (though what you say is reasonable).  People find
it fairly easy to understand sequntial actions, and I think it
often helps to use LET* when what you want to say is "first do
(or compute) this and remember the result, then do/compute that
and remember the result, ...".  The really confusing case is when
there's some variable, say X, that's bound in the LET* with another
instance of X outside the LET* so that references in the init forms
might refer to either one.  So long as refs are always to names bound
earlier in the LET* or to names that are not bound by the LET* at all,
there's a very wide range of cases where LET* is not difficult to
understand.

4. I don't think CATCH and THROW must be avoided, although these days
it may be better in many cases to use the condition system instead.
It's also better to use BLOCK and RETURN-FROM when you don't have to
contort the code to do so.

5. Bear in mind that DECLAIMing something INLINE may not result in
it being inlined.  If you really need the extra efficiency you may
have to use a macro or a compiler macro.  (Unless someone posts a
DEFSUBST (which defines a function and a matching compiler macro)
that we can all use.)

6. Note that type declarations _can_ lead to reduced efficiency
(even when you're optimizing for speed).  The reason is that the
variable in question may be stored in a space-efficient, type-specific
way so that every time you return (an element of) the value of the
variable a new object must be created on the heap.

This is not necessarily a reason to avoid declarations, but it is
something to bear in mind.

However, I think it removes one of the advanatges of Lisp if you
end up declaring everything, whether for documentation or otherwise.

7. A lot of your advice on sequences seems to be saying "use
sequence functions instead of LOOP" (although LOOP is explicitly
discussed as an alternative in only a few cases).  I think that
much of the time the choice between LOOP and something else is
largely a matter of taste.

8. There _are_ cases when it is reasonable to use EVAL.

Note too that it's always possible to avoid EVAL by using the
following trick:

  (defun secretly-eval (form)
    (funcall (coerce `(lambda () ,form) 'function)))

This suggests that you will untimately need some guidelines about
when it's reasonable to construct code and eveluate it even if you
rule out the use of EVAL per se.

9. One area that I think needs some emphasis is indentation.
Indentation is extremely important in Lisp, more so than in other
languages, and I see a lot of bizarrely indented code even from
people who are using a textbook full of good examples they could
imitate.  The aim of indentation should be to make it possible
for someone reading the code to pay very little attention to
individual parentheses.  All they should have to notice is the
general shape of the paren groups and how forms line up.

10. Another issue that needs more attention is that of organizing
systems that involve many files and more than one package.  There's
been some discussion of this recently in comp.lang.clos.  (There's
a lot of good advice in Sonya Keene's book on CLOS, but one thing
she doesn't discuss is how to use packages and CLOS together.
She regards packages as a general Common Lisp issue, but I think
it's become clear that CLOS raises some new issues regarding
package use (or else it makes certain "old" issues more significant).)

---

I think it's important to bear in mind that different audiences
have different needs.   In some contexts, it is right to have some
rules (such as "never use EVAL") that would be too strong in others.
That I disagree with some of your advice when it's directed to the
net in general does not mean I'd say it's wrong for your big Lisp
project.

-- jd