Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!ukc!edcastle!aiai!jeff From: jeff@aiai.ed.ac.uk (Jeff Dalton) Newsgroups: comp.lang.lisp Subject: Re: Virtues(?) of Lisp syntax Keywords: syntax words, functions, redundancy, cognitive engineering Message-ID: <3444@skye.ed.ac.uk> Date: 24 Sep 90 20:34:53 GMT References: <3427@skye.ed.ac.uk> <3465@syma.sussex.ac.uk> Reply-To: jeff@aiai.UUCP (Jeff Dalton) Organization: AIAI, University of Edinburgh, Scotland Lines: 182 In article <3465@syma.sussex.ac.uk> aarons@syma.sussex.ac.uk (Aaron Sloman) writes: >jeff@aiai.ed.ac.uk (Jeff Dalton) (14 Sep 90 20:21:11 GMT) >commented on my remarks on the syntactic poverty of Lisp. >> One of the *mistakes* some people make when writing Lisp is to >> try to add the redundancy you describe by putting certain close >> parens on a line of their own followed by a comment such as >> " ; end cond". It makes the code *harder* to read, not easier. > >(He goes on to justify this by saying that indentation plus a good >editor helps, and that GOOD lisp programs have short procedure >definitions and that adding such comments makes them longer and >harder to take in.) Humm. After reading this message from you, and some e-mail, I've decided that I must not have been as clear as I thought, especially when I brought in the question of procedure length. What happens in Pop-11 is that every "if" ends in "endif", every "define" ends in "enddefine", and so on. I think that doing that (or something close to it) in Lisp is (a) unnecessary, and (b) a mistake. It makes the code harder to read, but not just, or even primarily, because it makes the code longer. The main problem is that the code is more cluttered, the indentation obscured, and it becomes harder to pick out more important names that appear in the code, because there are all these other things made out of letters. In the Lisp indentation style used in, for example, most of the good Lisp textbooks, close parens are not emphasised (they're just on the end of a line containing some code) and so don't get in the way when reading. They contribute to the overall "shape" of the code, but don't have to be processed individually by the (human) reader. If instead they're put on lines of their own, and especially if an "end" comment is attached, then they become more significant and it takes more work to precess them. Moreover, to the extent that one has to read the end marker, to see whether it's an "endif" or an "endfor", one has failed to see this scope information in the indentation itself; so I'm not sure the increase in redundancy is as much as one might think. Anyway, I don't know whether having distinct end brackets ("endif" vs "endfor") is better than what's done in Pascal (all such groupings use "begin" and "end" -- Pascal seems to have dropped the Algol 60 provision for comments following the "end") or C (all groupings use "{" and "}"). I just don't think it works that well in Lisp. But what about "long" procedures? One thing to note is that it isn't just a matter of the number of characters or lines involved. If we have a DEFUN, then a LET, and everything else is in a CASE, and each CASE clause is fairly short, the whole procedure can be fairly long without becoming especially hard to read. So it's really a question of complexity, in some sense. A good way to make long procedures more readable is to break them up, visually, into manageable sections. Blank lines and a brief comment introducing a section are more effective, in my experience, than emphasising end markers. Another important technique is to make the procedures shorter by using appropriate macros and higher-order functions (and, of course, by putting some things into separate procedures). In many languages, loops are written out using a "for" statement (or equivalent), and the programmer has to recognize certain common patterns of code. Lisp is often written that way too, but since it's fairly easy to write a function or macro that embodies the same pattern in a shorter, more easily recognized, form, the Lisp programmer has some effective alternatives to writing out loops in-line. Hence function such as MAPCAR, macros that implement "list comprehensions", and so on. >> Of course, it's no doubt possible to write procedures (such as ones >> that are too long) where end markers might make a difference. But >> is is also possible to avoid those cases, just as it is possible to >> avoid other bad practices. >I agree that a good editor helps a lot (though people often have to >read code without an editor, e.g. printed examples in text books, The editor is to help you *write* code that can be read. The whole point of good indentation is to make it possible to read the code without having to pay attention to individual parentheses. And the code is readable whether it's in a book, a file, or whatever. The reason people without a good Lisp editor may find end markers helpful is that they have difficulty matching parens when they're writing and find it difficult to check whether they've made a mistake. And, since they're new to Lisp, they may also find that syntax easier to read. But I certainly didn't mean to say (if I did say it) that a good editor was necessary in order to read the code. (Which is not to say there *couldn't* be an editor that helped.) >etc) and I agree that well-written short lisp definitions >(e.g. up to five or six lines) are often (though not always) easily >parsed by the human brain and don't need much explanatory clutter. I don't know where you got the "five or six lines" from; it's certainly not the limit I had in mind for "short". And *of course* such functions are not *always* easy to read; good indentation, and other good practices, are still necessary. There are readable Lisp procedure definitions up to a page long in some textbooks (and of course in other places). Once definitions are longer than a page, they are usually significantly harder to read in any language, although there are certain combinations of constructs for which long definitions aren't so great a problem. (Such the the CASE example mentioned above.) I don't think Lisp is necessarily much worse than other languages in this respect (if it's worse at all). >But I doubt that it is always desirable or possible to build good >programs out of definitions that are short enough to make the extra >contextual reminders unnecessary. Given your "five or six lines", I'm not surprised to find you think this way. And there are contextual reminders other than end markers, such as introductory comments, which I find more effective. >Even Common_Lisp_The_Language (second edition) has examples that I >think are long enough to benefit from these aids that you say are >unnecessary. (Scanning more or less at random for pages with lisp >code, I found examples on pages 340-349, 667, 759, 962 and 965. Or >are these just examples of bad lisp style? (I've seen much worse!) I have also seen code that is harder to read. Look at "AI Practice: Examples in Pop-11" sometime. [I'm sorry. I'm sure this book has many virtues. But some of the procedures cross a couple of page boundaries or are, in other ways, somewhat hard to read.] Moreover, we have to be clear on just what aids I think are unnecessary. I think the imitating Pop-11 end markers is not a good idea (because there are so many of them) but that end markers are sometimes helpful. This was discussed in more detail above. >My general point is that human perception of complex structures is >easiest and most reliable when there is well chosen redundancy in >the structures, and most difficult and error-prone when there isn't. >However, as you point out, too much redundancy can sometimes get in >the way, and we don't yet know the trade-offs. Here I agree. Well-chosen redundancy is important. However, it's important to bear in mind that many programmers new to Lisp haven't yet learned to "see" the redundancy or else chose programming styles that obscure it. The redundancy comes from three things: * Arity (eg CAR has one argument). * Indentation. * The overall shape of the parenthesis groups (note that this does not require that the reader pay much attention to individual parens) To show what I mean by the last, here's an example. Code that looks like this: ( (( ) ( )) (( ) ( )) ( ( ))) is usually a COND. (COND is perhaps the least readable Lisp construct.) Breaking up the paren groups tends to remove this information. Putting in end markers adds a different redundancy but tends to make the indentation less effective. Anyway, I'm not interested in a language war or in a discussion that more properly belongs in alt.religion.computers. I just want to keep some space for the view that Lisp is (or can be) readable and to suggest how some people may have been approaching the language in less effective ways. -- Jeff