[comp.lang.lisp] Reading Lisp: parens and indentation

jeff@aiai.uucp (Jeff Dalton) (06/09/89)

In article <1057@syma.sussex.ac.uk> aarons@syma.sussex.ac.uk (Aaron Sloman) writes:
>jeff@aiai.ed.ac.uk (Jeff Dalton) writes:
>> Now, it's interesting that you're supposing greater cognitive
>> effort is required when reading Lisp, since many people have
>> found that just the opposite is true.

>I was assuming everyone agreed that syntactic redundancy (within
>reasonable bounds) reduces cognitive effort.

Then why do so many people find that there is *less* effort involved
in reading Lisp.  There still needs to be an answer to this.  It
should be clear, I think, that redundancy isn't all there is to it.

>Indentation is used by lisp programmers in an attempt to increase
>redundancy for that very reason.

Programmers don't think "I'd better add some redundancy", and it
may be that their view of things is a more enlightening one.

Indentation is used in an effort to make programs more readable.
It works because it gives some information in a different, more
readable, way.  It's not just an increase in redundancy, because
it's goal is to make it possible to ignore the parentheses, not
to give you two ways to "read" them.  Now, in some cases there's
redundancy.  For example, you know that CAR takes one argument,
and the indentation also shows that the (one) argument goes with
CAR.  But in other cases, it's the indentation alone (given that
we're ignoring the parens) that says what goes with what.

Besides redundancy can get in the way endbesides.

>But I previously pointed to local ambiguities that indentation alone
>does not resolve. (E.g. indentation as such does not tell you, as a
>keyword does, that some local variables are being declared.

Sure it does:

   (labels ((f (x) (+ x 1))
            (g (x) (f (f x))))
     (f (g x)))

The indentation is the main signal that f and g are being "declared"
in the first two lines.

>> You also seem to be ignoring the points made about indentation.
>
>Not ignoring them. I think indentation helps (even in richer
>languages). But the point about indentation ignored my point about
>needing to use extended context to disambiguate. Indentation gives
>information about relative scope - but you still have to look upward
>to see that "((" means "elseif" rather than something else. And as
>you say, short procedures help.

I din't ignore your point.  Indeed, I replied to it, and you
quoted my reply in your message (see below where I quote the
quote).

It is true that keywords, like "then" and "else" can make it easier
to keep one's place.  And it's easy to add such keywords to an IF
macro.  Franz Lisp had them.  Many Lisp programmers don't use this
approach, but it can be, and has been, done.

However, you don't have to look up (to see the "COND"?) to see that
"((" means "elseif".

First, consider Common Lisp.  Common Lisp has some redundancy (you
might call it) that Scheme lacks, namely it requires FUNCALL when
the function to be called is determined by some expression rather
than just a name.  Consequently, "((" is very rare in Common Lisp.

But, really, "((" doesn't mean "elseif".  Rather, it's being in a
certain position in a COND that means "elseif".  If you jump into the
middle of a long COND, the "((" may be an important clue to where you
are.  But if you start reading from the beginning, it's less
important.  Then you just remember that you're in a COND, and
everything, not just the "((", tells you where you are.  You know
you've just finished one clause and hence that the next line will
begin a new one.  And, sure enough, the indentation gets less and
there you are.

>> When reading well-formatted Lisp, it is almost never necessary
>> to read the parentheses;

>Has anyone tried using a lisp using only indentation and no
>parentheses of the kind that you don't need to read? It would be an
>interesting experiment, though I fear the result might be Logo.

Lisp uses a Polish prefix notation.  If the number of arguments for
each operator were fixed, there would be no need for parentheses.
Without parentheses, you'd write functions like this:

   defun factorial x
     if = x 0
        1
        * x factorial - x 1

Now, it's clear that the parentheses add something.  But, to make
my point more precisely, they don't have to be read one-by-one.
It's not as if each were a "begin" or "end".  They're smaller
and less significant.

Lisp programmers are not constantly thinking "this parenthesis closes
the call to f" as they read.  Groupings are made in part by indentation
and in part by the shapes of parenthesis groups.  It's not necessary
to read every parentheses individually.

I've seen some people add redundancy to closing parens by putting
them on a line by themselves along with a commment saying what they
close.  I find the result *much* harder to read than one that puts
less emphasis on the "end brackets".

>> .....and if functions are short (as they
>> most often should be), it's easy to keep one's place without
>> syntactic helpers like "then" and "endif".
>
>I agree that short lisp function definitions are easy to read.

Long procedures are harder to read in any language.

>I guess the lisp code I have found hard to read was not written
>by well-trained lispers (even though some of them wrote text
>books)

It's not the case that everyone finds the same things easy to read.
Still, no doubt one has to become used to Lisp before it becomes easy.
But that much is true of all programming languages.  It may be a
greater problem with Lisp, but since we lack hard data I don't think
we can say much more than that.

There are a number of reasons why Lisp code in textbooks might be
hard to read.  For one thing, much of it is in upper case.  For
another, much of the code isn't well-written.  Moreover, many
texts are not well-formatted, use unreadable fonts, etc.

-- Jeff

ram@wb1.cs.cmu.edu (Rob MacLachlan) (06/09/89)

Although I am quite happy with Lisp syntax, I believe that predjudice
against Lisp syntax is very real.  I think that this may be a major reason
behind the "ghettoization" of the Lisp community.  I know an experienced
Lisp programmer who was doing some non-lisp-specific work on programming
SIMD processors.  Pursuant to this, he had to design an implement a new
langauge.  He initially used Lisp syntax, since he was comfortable with it,
and it allowed him to use the lisp reader as his parser, but he found it
difficult to get anyone without a Lisp background to read his paper.  He then
modified a parser generator to emit Lisp code, changed his syntax and had a
much better reception.

I do believe that Lisp syntax is initially somewhat harder to comprehend
that more conventional syntaxes, although this might be less of a problem in
a simple teaching subset that omitted warts such as COND.  But I also
suspect that Lisp syntax is something of a local optimum, given the
advantages of easy code manipulation.  The thing is, that the list
representation of Lisp has a moderate similarity to the underlying
semantics.

Modern Lisp compilers have moved away from using lists as the internal
representation for code.  The most important shortcoming of lists is that it
is difficult to annotate a list with the result of any semantic analysis.
It would be useful for users to be able to write "macros" that transformed
these richer intermediate representations, but this is difficult because the
richer representation is harder to comprehend.

  Rob MacLachlan   CMU Common Lisp group
--