From: Marc Riese
Subject: Book review: "Lisp Style and Design"
Date: 
Message-ID: <3473@disuns2.epfl.ch>
One of the FAQ's on this group is something like "Does anyone know
any good books about how to program well in Lisp?". The most common
response is a pointer to Miller and Benson's "Lisp Style and Design".

I have read it recently and wrote a small review for my own notes,
consisting of a summary, some comments, and some less significant details.
I am posting it in case anyone is interested. Personally, I found
that the book is good, but has significant problems.
My opinion is that of a software engineer. Although I have basic knowledge
of Lisp and CLOS, I am certainly not a Lisp expert.

Perhaps someone could tell us if other such books exist,
if the authors are planning a revised edition, and
if other reviews have been done on this book and where they can be found.

Marc Riese
Swiss Federal Institute of Technology
Lausanne

---------------------------------------------------------------


Title:           Lisp Style & Design
Authors:         Molly M. Miller and Eric Benson
Keywords:        programming book
Source:          Digital Press; 1990; ISBN 1-55558-044-0
X-Refs:          keene88

Summary
-------
a. Book about how to program well in Lisp. Miller wrote the text; Benson coded.
   The authors state that the book is not a Lisp primer and that it
   should benefit those with a basic knowledge of Lisp. However, readers with
   no background in software engineering are advised to supplement this
   Lisp-specific book with any text explaining and justifying the necessity
   of good programming style in general, and the place of good programming
   style in the context of good software engineering over the entire
   software lifecyle.

b. Good programming style aids readability, maintenance and debugging p1.
   Good style comprises efficient algorithms, good organisation, appropriate
   abstractions, well-constructed function definitions, useful commentary, and
   effective degugging p5. Good style must involve all of the following:
   good organisation; good function definitions; adequate algorithms p6.

c. The Preface claims that the authors show "how to design, write, document,
   and debug well-organized programs that use the complete expressive power
   of Lisp effectively." Use of the word "complete" was optimistic for this
   200-odd page book.

d. "You should never have to sacrifice clarity for the sake of efficiency".p138

e. Scattered references to unnecessary but useful compiler optimization 
   possibilities: defining slot types in advance; defining array entry
   types in advance; using vectors and hashtables instead of lists. These
   are completed in Chapter 7.

f. Chapter 3 discusses data structures, operator constructs, and idioms. Much
   good advice is given, although the organisation of details is not clear
   and (fortunately?) it is not complete. Some of the chapter only consists of
   listing the names of a subset of Lisp features. For example, all we learn
   about bit vectors is a collection of key words associated with them.

g. Index is good. Expression is usually clear and shows realism, for example:
  "What seems like a reasonable approach while you are mulling over the
   problem in the shower often looks less robust when you are staring at
   the debugger prompt." p20 The vocabulary is unclear at times. For example,
   one sentence starts as follows: "Similarly, the connotation associated 
   with a construct can become polluted if ..." p31. 
   Exercises are limited to one page as the last appendix.

Comments
--------
a. Good programming style for the authors is limited to 
   aiding readability, maintenance, and debugging - a very short list. 
   (cf Preface & Intro) Good style should give significant priority to 
   reusability, extensibility, and portability. Reusibility is only alluded to
   as "sharing common properties and behavior" p87 and "generalizing" p148. 
   Extensibility is first referred to on page 22 as an attribute of CLOS, and 
   this in a subsection about data structures. Portability is only mentioned 
   as an attribute of Common Lisp. I was very surprised to see that the major
   example of this text depends on a particular compiler! (Lucid)
   If experts cannot write portable code in Lisp, who can?

b. I found the many references to the authors' company, Lucid Inc., to be of
   annoying. Some are simply not necessary.

c. I have seen that the example program was also very complex for students
   having basic knowledge of Lisp. If an exemplary user interface requires 
   such complexity, imagine what non-exemplary work will produce! The example
   does not clearly show how the input data file is created.

d. Bibliography is extremely short granted the wealth of literature on
   programming style. This meagre reading shows itself in the text.
   The book discusses programming style; since this topic is a partial 
   introduction to good software engineering in general, the book should
   show the place of the programming activity in the  context of the 
   software life cycle, but it does not. Chapter 2 informally mixes the
   identification of problem requirements (analysis) with defining a solution
   (design); not good for an introduction and a book that emphasizes
   organisation. It suddenly discusses the design process without having 
   defined the term. It refers to iterative design without reference to
   iterative s/w life cycles, such as the spiral method. Similarly, the
   expression "programmatic interface" is used before it is defined. Such 
   informality is of questionable acceptability for a book about good style. 
   It seems to me that good style requires discipline (hence structure, 
   organisation, and at least some sort of formality) in order to achieve
   its goals.

e. When justifying the use of CLOS, the authors discuss
   inheritance and extensibility. However, "In addition to these legitimate
   concerns, we simply want to try out this new capability of Common Lisp."p22
   Here we have a book about good style stating that one reason why the
   authors are using a certain programming technique is because they
   "want to try [it] out". Considering how many software projects fail because
   of unnecessary complexity, this sentence should not appear in a book
   about style. On a related note, the authors rightly point out that 
   programmers should be careful to maximise (optimise?) their use of the 
   existing features of Lisp.

f. Although tail recursive functions are defined and used, the importance of
   this technique over less efficient recursive functions is not mentioned.
   This is an important point that is not always obvious to those with
   only basic knowledge of Lisp. Similarly, the authors do not clearly show
   when to use mixin classes as opposed to non-mixin classes. p69

g. A Lisp style book espousing readability should imitate Winston and 
   encourage use of "first" and "rest" when discussing lists, and leave
   mention of "car" and "cadr" for the folklore section. The authors present
   the latter operators first. (p32,42)

h. Needs a more in-depth treatment of good CLOS programming style.

i. Despite the above criticisms, the book contains much good advice.

Details
-------
a. If you want a non-nil value to be returned, use "and" instead of "if".
   When the value returned is not important, use "when" and "unless", which
   are intended to produce side effects, instead of "if". p39.

b. Whenever possible, use defvar and defparameter for special or global
   variables. The documentation function indicates that such variables
   are indeed variables, as opposed to those set by setq. p43-45 
   However, let and let* are preferred for temporary or local variables.p43 

c. defvar does not change global variables already having a value.
   Beware of this when assuming newly loaded code resets old values!

d. Use setf instead of wasting space and cluttering legibility with
   home-made access and update operators. p43

e. mapcar syntax can be more clear than other recursive or iterative 
   constructs.

f. If a function is only called by one other, the authors advise nesting 
   the former in the later using flet or labels p51. I think this can make
   for functions too large to be readable.

g. Although the same name may be used for different entities having different
   scope, the authors correctly point out that whoever maintains the code
   may not realise this. p54

h. Use recursion for hierarchical data structures and iteration for lists.

i. Objects that are difficult to create and allocate at run time, like bitmaps,
   can be kept in a pre-allocated resource pool. p63

j. Good advice on how to avoid problems when writing macros. p83

k. Global variables should be enclosed by asterisks. Predicate names should
   end with "p". p101

l. p102 The first line of a file starting 
   ";;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: PLAN; Base: 10 -*-"
   is called the file options which is parsed by Emacs and Lisp editors to
   determine the editor mode, file buffer syntax, Lisp package of the buffer
   and the numeric base used in the file.
   ;;;; Four semicolons defines file subheadings, like collections of
                         definitions.
   ;;;  Three semicolons are used to document the following function, macro,
                         or variable.
   ;;   Two semicolons are used to describe the following expression
   ;    One semicolon is used for in-line comments

m. An example of dividing code into files (and loading) is given on p107.
   Packages are mainly useful for avoiding symbol name clashes. p110
   Use of declaration files, for extremely large code, ensures proper
   compilation and loading of program constructs. p112

n. For debugging, use: describe, inspect, apropos, apropos-list, trace,
   and step. Common lisp provides constructs for detecting errors and
   going directly to the debugger with them: break, warn, error, cerror,
   assert, check-type, etypecase, ecase, ctypecase, and ccase.

o. For efficiency make use of: "time" and "room"; declarations (type,
   optimise); and compiling.

From: Jeff Dalton
Subject: Re: Book review: "Lisp Style and Design"
Date: 
Message-ID: <6616@skye.ed.ac.uk>
In article <····@disuns2.epfl.ch> ·····@litsuns1.epfl.ch (Marc Riese) writes:
>g. A Lisp style book espousing readability should imitate Winston and 
>   encourage use of "first" and "rest" when discussing lists, and leave
>   mention of "car" and "cadr" for the folklore section. The authors present
>   the latter operators first. (p32,42)

FIRST and REST are right for some cases, wrong for others.

And what's so bad about car and cdr anyway?
From: Marc Riese
Subject: Re: Book review: "Lisp Style and Design"
Date: 
Message-ID: <3655@disuns2.epfl.ch>
I wrote:
>g. A Lisp style book espousing readability should imitate Winston and 
>   encourage use of "first" and "rest" when discussing lists, and leave
>   mention of "car" and "cadr" for the folklore section. The authors present
>   the latter operators first. (p32,42)

Jeff Dalton writes:
>FIRST and REST are right for some cases, wrong for others.
>And what's so bad about car and cdr anyway?

In "Lisp" 3rd Ed. page 21, Winston states
  "FIRST and REST were originally called CAR and CDR because of the way
   they were implemented on the ancient IBM 704 computer."

On page 104, Winston states:
  "Mapcar is perhaps the most unfortunately-named primitive in Lisp.
   The map part is alright ... The car part, however, comes from the
   now-obsolete CAR primitive ... MAP-ELEMENTS would have been a better name."

In "AI Programming" page 14, Norvig states:
  "I'm sure you'll agree that first and rest are much better names, and they
   will be used whenever we are talking about lists. However, we will continue
   to use car and cdr on occasion when we are considering a pair of values
   that are not considered as a list."

I think that, aside from the uses Norvig mentions, car and cdr are "bad"
because their names are obscure and therefore they diminish sw readability.
Good software should be written with its future readers in mind.

This was an annoying, but not very important, aspect of a useful book.
While we're at it, I wish the comp.lang.lisp FAQ style recommendations
would at least mention this.

Marc Riese
Swiss Federal Institute of Technology
Lausanne
From: Barry Margolin
Subject: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <kvopnnINNpa9@early-bird.think.com>
In article <····@disuns2.epfl.ch> ·····@litsuns1.epfl.ch (Marc Riese) writes:
>
>I wrote:
>>g. A Lisp style book espousing readability should imitate Winston and 
>>   encourage use of "first" and "rest" when discussing lists, and leave
>>   mention of "car" and "cadr" for the folklore section. The authors present
>>   the latter operators first. (p32,42)
>
>Jeff Dalton writes:
>>FIRST and REST are right for some cases, wrong for others.
>>And what's so bad about car and cdr anyway?
>In "Lisp" 3rd Ed. page 21, Winston states
>  "FIRST and REST were originally called CAR and CDR because of the way
>   they were implemented on the ancient IBM 704 computer."

Their etymology is not really significant.  Many Lisp experts consider it a
Good Thing that CAR and CDR are essentially meaningless.  There's no
real-world metaphor for them; a cons is like a box with two equivalent but
distinct compartments.  They could be called CONS-LEFT-PART and
CONS-RIGHT-PART, but there's nothing really left/right about them except
that this is the way they're often represented on 2-d paper, screens, or
blackboards (but I wouldn't be surprised if they were represented
"backwards" in Hebrew or Arabic speaking areas); in the box analogy, a box
can be rotated, but its compartments retain their identity.  They could be
called CONS-PART1 and CONS-PART2, but this implies an ordering to a cons
that doesn't exist (except that this is the order of the arguments to the
CONS function -- but if keyword arguments had existed in the 60's then
perhaps it could have taken :CAR and :CDR keywords so that the ordering
wouldn't be necessary).  Perhaps if the nonsense words "foo" and "bar" had
been in use in the early 60's they might have been called CONS-FOO and
CONS-BAR.

>On page 104, Winston states:
>  "Mapcar is perhaps the most unfortunately-named primitive in Lisp.
>   The map part is alright ... The car part, however, comes from the
>   now-obsolete CAR primitive ... MAP-ELEMENTS would have been a better name."

Well, the mapping functions are full of poor names.  How is one to remember
which of MAPC/MAPCAR and MAPL/MAPLIST return the results (my mnemonic is
that the ones with long names build results)?  MAPCAN and MAPCON are like
MAPCAR and MAPLIST except that they NCONC the results rather than listing
them, but how can you tell which maps to which?  Had the Common Lisp
designers wished to tackle this, I suspect we'd have gotten a generic
MAP-LIST function with KEY and RESULT-BUILDER arguments.  If we just wanted
better names for the existing functions, we'd have something like
MAP-ELEMENTS, MAP-TAILS, MAP-ELEMENTS-LIST-RESULTS, MAP-TAILS-LIST-RESULTS,
and MAP-ELEMENTS-NCONC-RESULT, and MAP-TAILS-NCONC-RESULTS.

>In "AI Programming" page 14, Norvig states:
>  "I'm sure you'll agree that first and rest are much better names, and they
>   will be used whenever we are talking about lists. However, we will continue
>   to use car and cdr on occasion when we are considering a pair of values
>   that are not considered as a list."
>
>I think that, aside from the uses Norvig mentions, car and cdr are "bad"
>because their names are obscure and therefore they diminish sw readability.
>Good software should be written with its future readers in mind.

I think this is somewhat a religious matter.  I think any respectable Lisp
programmer should be expected to understand the relationship between conses
and lists; this is too fundamental an aspect of Lisp to be completely
abstracted away.  Therefore, there should be nothing confusing about the
use of CDR on lists.

CAR and CDR are part of the normal Lisp vocabulary.  We have phrases like
"cdr'ing down a list" (which was probably more widely appreciated in the
days before DOLIST and LOOP, when you actually had to write the calls to
CDR in your DO loops).  Even if you don't personally like them, they're
common enough idioms that you can't simple relegate them to a footnote or
appendix on archaic style.  Idioms are like that; taken out of context,
they are generally horrible style, but once you realize that they're so
commonly used that everyone just recognizes them as a unit then they don't
matter.

Maybe I'm just stuck in my ways.  When I learned Lisp (about 12 years ago),
FIRST and REST weren't even mentioned.  I don't think they were in the
MacLisp environment by default, but many people defined them as macros that
expanded into CAR and CDR.  I never got into the habit of using FIRST and
REST, but still consider my code good style.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Marc Riese
Subject: Re: CAR/CDR vs FIRST/REST
Date: 
Message-ID: <3659@disuns2.epfl.ch>
Barry Margolin writes:
> Many Lisp experts consider it a Good Thing that CAR and CDR are
> essentially meaningless.  There's no real-world metaphor for them;
[interesting stuff about alternative names]

> the mapping functions are full of poor names.
[more interesting stuff about mapping names]

> I think any respectable Lisp programmer should be expected to understand
> the relationship between conses and lists; this is too fundamental an 
> aspect of Lisp to be completely abstracted away.

All the points above are well taken. Like I said in my original posting,
I am certainly not a Lisp expert. I'm learning.

> they're common enough idioms that you can't simple relegate them to 
> a footnote or appendix on archaic style.

Perhaps that's what mislead me in the first place, because Winston
does just that! Anyway, thanks for the information.

Bob Riemenschneider writes:
>anyone who finds that "old-fashioned"
>uses of CAR and CDR significantly diminishes readability has no business
>reading Lisp!

Winston states that they are "obsolete" and "ancient".
Norvig says that they were based on the original implementation, and then
writes "I'm sure you'll agree that FIRST and REST are much better names".
Obviously, readibility is a significant criterion for them here.


Marc Riese
Swiss Federal Institute of Technology
Lausanne
From: Marty Hall
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <1992Apr28.134125.20260@aplcen.apl.jhu.edu>
·····@litsuns1.epfl.ch (Marc Riese) writes:

>g. A Lisp style book espousing readability should imitate Winston and 
>   encourage use of "first" and "rest" when discussing lists, and leave
>   mention of "car" and "cadr" for the folklore section. The authors present
>   the latter operators first. (p32,42)
[...]
>In "AI Programming" page 14, Norvig states:
>  "I'm sure you'll agree that first and rest are much better names, and they
>   will be used whenever we are talking about lists. However, we will continue
>   to use car and cdr on occasion when we are considering a pair of values
>   that are not considered as a list."

······@think.com (Barry Margolin) replies:
>I think this is somewhat a religious matter.  I think any respectable Lisp
>programmer should be expected to understand the relationship between conses
>and lists; this is too fundamental an aspect of Lisp to be completely
>abstracted away.  Therefore, there should be nothing confusing about the
>use of CDR on lists.

Well, I agree with Barry that it is somewhat of a religious matter, and
that for the serious LISP programmer it would not be confusing. However,
I have two small reasons why I tend to prefer first/rest on lists.

(A) It reminds me (and the reader) what I'm doing. Ie if they see car/cdr
that would indicate that conses are being used for something besides lists.
I think this is more or less what Peter Norvig was getting at above.

(B) There is, IMHO, more of a role for abstracting lists for beginners than
for experts. I teach AI and AI programming to part-time MS students here at
Johns Hopkins, and think it is preferable to introduce lists as an abstract
entity first, and only talk about how it is actually implemented somewhat
later. In fact, I sheepishly admit that, early on, most of my students
don't know that car/cdr are two "equal" slots, that the second argument
to cons doesn't have to be a list, etc. Basically, they don't even know
what conses *are* for quite a while! Now, these are AI and AI Programming
courses, not LISP courses, so LISP is mostly just a tool to illustrate
the AI techniques (especially in the first course). But nevertheless, I
think there *is* some benefit to abstracting lists in this non-LISP-expert
crowd, at least at the beginning.

					- Marty
------------------------------------------------------
····@aplcen.apl.jhu.edu, ···········@jhunix.bitnet, ..uunet!aplcen!hall
Artificial Intelligence Lab, AAI Corp, PO Box 126, Hunt Valley, MD 21030

(setf (need-p 'disclaimer) NIL)
From: Hans Tallis
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tallis.704494434@starbase>
In <············@early-bird.think.com> ······@think.com (Barry Margolin) writes:
>>On page 104, Winston states:
>>  "Mapcar is perhaps the most unfortunately-named primitive in Lisp.
>>   The map part is alright ... The car part, however, comes from the
>>   now-obsolete CAR primitive ... MAP-ELEMENTS would have been a better name."
>Well, the mapping functions are full of poor names.  


Amen.  I deem it unacceptable to use any mapping functions whatsoever.  Ditto
for all _do_ forms.  They are tough to write and impossible to read.  There's
no excuse for not using a loop macro, which is instantly readable by anyone.

--Hans
From: Scott McKay
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <19920428225447.8.SWM@SUMMER.SCRC.Symbolics.COM>
    Date: Tue, 28 Apr 1992 16:53 EDT
    From: Hans Tallis <······@starbase.mitre.org>

    In <············@early-bird.think.com> ······@think.com (Barry Margolin) writes:
    >>On page 104, Winston states:
    >>  "Mapcar is perhaps the most unfortunately-named primitive in Lisp.
    >>   The map part is alright ... The car part, however, comes from the
    >>   now-obsolete CAR primitive ... MAP-ELEMENTS would have been a better name."
    >Well, the mapping functions are full of poor names.  


    Amen.  I deem it unacceptable to use any mapping functions whatsoever.  Ditto
    for all _do_ forms.  They are tough to write and impossible to read.  There's
    no excuse for not using a loop macro, which is instantly readable by anyone.

This is a joke, right?  I have seen uses of LOOP, some of them
written by me in an earlier life, that can only be described as
abominations.  I use DOLIST, DOTIMES, and MAP frequently and would
hardly call them unreadable.

And hasn't this whole discussion has become pointless anyway?  We
all know that bad programs can be written in any language, as well
as good programs.  It's a matter of discipline on the part of the
programmer, not on piddly things like whether you use FIRST instead
of CAR, REST instead of CDR, or LOOP instead of DOLIST.
From: Hans Tallis
Subject: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <9204291349.AA05211@starbase.mitre.org>
> This is a joke, right?

Ah, no.

> I use DOLIST, DOTIMES, and MAP frequently and would
> hardly call them unreadable.

The simplest dolist forms I can't remember:  which is the iteration variable,
the iteration form, the return result?  None of this stuff is explained in the
call to any map or do form, whereas it's all explicitly there in a loop call.

I'm willing to bet your unreadable loops were doing things not easily possible
with map/do forms. In that case, the loop should have been split into two or
more. 

--Hans
From: David Schulenburg
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <1992Apr28.232049.20779@aero.org>
In article <················@starbase>, ······@starbase.mitre.org (Hans Tallis) writes:
|> 
|> Amen.  I deem it unacceptable to use any mapping functions whatsoever.  Ditto
|> for all _do_ forms.  They are tough to write and impossible to read.  There's
|> no excuse for not using a loop macro, which is instantly readable by anyone.
|> 
|> --Hans

WRONG!  loop is one of the major abominations in common lisp, what with all
of its reserved words.  how are the following "instantly readable"?  it is not
instantly apparent what the semantics of these are, let alone how one should
use them.  talk about impossible to read!  this is what happens when one gets
the use of special forms and macros.  you have to KNOW what the syntax and
semantics of these forms are in order to use/read them.  the map FUNCTIONS
don't suffer from this affliction -- they're functions like (most of) everything
else in lisp.  of course, the semantics are important as are the order of
arguments.  if this is considered a problem, then one probably shouldn't be
using lisp at all as this "problem" applies to all functions in lisp.

although i prefer to use recursion in most instances, i find do useful as it is
the closest of these forms to recursion.

here are a few of the more arcane loop clause reserved words.  there are even
restrictions on where/when some of these can be used.  ugh!

thereis
minimizing
always
maximize
summing
nconcing

david

as a colleague recently remarked:  "to iterate is human; to recurse, divine."
From: Hans Tallis
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tallis.704556977@starbase>
Sigh.  Let's review our basics:

1. Code must be easy to read by the next person.
Exercise for the reader:  write the loop equivalents for the comon mapping
(mapcar, maplist, mapc, mapl, mapcan, mapcon) and iteration (do, dolist,
dotimes) constructs and tell me which is easier to _read_.  For example,
compare 
	(setf list '(a b c)

	(loop for x in list
		collect (my-fcn x))
and
	(loop for x in list
		do (my-fcn x)
		return list)

with the equivalent map forms (I can't figure out what they'd be with less than
a few minutes study, but I think they're mapcar and mapc?)  Now try to argue
that the latter are more readable to the new reader, and that errors in coding
are more transparent with them.  Go ahead, I'm listening.  For my money, the
difference between the above loop forms is more apparent than the mapc/mapcar
typo will ever be.

Let's try another example:  how many people easily grok this example from
CLtL2 (p. 172): 

	(setf list '(a 1 b c 3 4 d 5))

	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)

Quickly, what does this return?
	- '((1) (3) (4) (5))
	- '(a 1 b c 3 4 d 5)
	- '(a b c d)
	- '(1 3 4 5)
	- '(nil 1 nil nil 3 4 nil 5)

Now compare

	(loop for x in list
		when (numberp x) collect x)

Can this point be made any clearer?  It is no argument to say that map forms
take fewer keystrokes to write.  Unless you've buried your head deep in the
Lisp sandpile for several years I think you'll have to admit that the second
form is easier to read.

2. Let me iterate this point: the dominant cost in software production is
_maintenance_, at least for us writing large systems.  The time spent writing
code generally makes up <= 10% of the system development time.  Ease of reading
is _everything_ when writing code.  Despite claims that this is all syntactic
sugar, anyone who's done serious software archeology will admit that healthy
amounts of sugar can save lots and lots and lots of $$ in the long run.

3. Loop is very powerful, granted, and many people are trying to argue that
"you can do so much with loop that it's unreadable."  
This is not an argument.
Loop, in fact, easily does lots of things that _none_ of the mapping and
iteration forms do.  Personally, I'm no great loop hacker, and I never exercise
most of the loop clauses available; generally I use the small subset of loop
keywords necessary to implement the 9 forms listed above (< half a dozen), and
rarely use _finally_ or _minimizing_ or what-have-you forms.  (Let's try to
keep the discussion rational.)

--Hans
From: Bruce R. Miller
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <2913553916@ARTEMIS.cam.nist.gov>
In article <················@starbase>, Hans Tallis writes:
> Sigh.  Let's review our basics:

Oh, please dont Sigh!! :>

> 1. Code must be easy to read by the next person. ...
> 2. Let me iterate this point: the dominant cost in ...
> 3. Loop is very powerful, granted, and many people are trying to argue that
> "you can do so much with loop that it's unreadable."  
> This is not an argument.
> ...  (Let's try to  keep the discussion rational.)

Ok, if you insist.
One of the double-edged swords in Lisp is that there is usually many
ways to do something.  And if we could form any sort of consensus
amongst us, there would _still_ be 2 or 3 ways that would be commonly
considered `readable'.
Amongst the `good' ways, people choose a style they're comfortable with
and stick with it.

So, for example, I personally am a pretty hard-core Loop user.  For
simple map & construct situations, I'll use MAPCAR.  For pure side
effect situations I'll use DOLIST.  I seldom use the other map & do
variants; I'll opt for LOOP.   For _ME_ that is the clearest & most
concise.  

On the other hand, I think it is in my best interests, perhaps even my
`responsibility' as a lisp programmer, to be familiar with those other
forms.  If I want to read someone else's program, be they `better' or
`worse' programmers than me, maybe they've made different choices.
You can use first/rest if you like, but you'd _better_ know what car &
cdr are!

LOOP for the most part is nice and readable.  LOOP wins over MAPxxx
when building complicated results, and when the end condition is
somewhat involved.  But the latter is exactly where it can become 
tricky; figuring out what the end state will actually be.
DO, for all its warts, at least makes clear what the end condition is
(once you find it!:>)

An anecdote;  I was talking with Gerald Sussman (of clean, lean & mean
Scheme fame) about Scheme's lack of macros from which one could build
eg. elegant iteration facilities.  Part of his reply was something to
the effect: "So you like LOOP, huh?  How many times have you had to
macro expand them to figure out what it was _really_ going to do?"
Without agreeing entirely with his programming philosophy, I did have to
concede that he had a point!
From: Michael Pazzani
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <29FED0F6.13953@ics.uci.edu>
In article <················@starbase> ······@starbase.mitre.org (Hans Tallis) writes:


>
>Let's try another example:  how many people easily grok this example from
>CLtL2 (p. 172):
>
>	(setf list '(a 1 b c 3 4 d 5))
>
>	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)
>

>Now compare
>
>	(loop for x in list
>		when (numberp x) collect x)


>Can this point be made any clearer?  It is no argument to say that map forms
>take fewer keystrokes to write.  Unless you've buried your head deep in the
>Lisp sandpile for several years I think you'll have to admit that the second
>form is easier to read.

I almost agree.  The mapcan form above is a common idiom. Another way (I hate)
to achieve the same effect is to remove nils from mapcar.  However, since this
idiom is so common, why not define another mapping function for it?  for example
(all-images #'(lambda(x)(if (numberp x) x nil)) list)

>3. Loop is very powerful, granted, and many people are trying to argue that


>Loop, in fact, easily does lots of things that _none_ of the mapping and
>iteration forms do.

I agree, use it when none of the mapping functions are appropriate.
If you find a commonly occuring pattern of loop usage, define a new mapping
function to abstract out that particular construct.

>"you can do so much with loop that it's unreadable."
Today's loop quiz: explain this result
(In MCL and Franz Allegro Common Lisp at least)

(let ((it nil))
  (loop for x in '(a b c 1 2 3)
        when (numberp x) collect it))
   => (T T T)


Mike
From: P. T. Withington
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <19920429171001.8.PTW@SAPPHO.Myteline.Symbolics.COM>
    From: ······@starbase.mitre.org (Hans Tallis)

    Let's try another example:  how many people easily grok this example from
    CLtL2 (p. 172):
    
    	(setf list '(a 1 b c 3 4 d 5))
    
    	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)
    

    Now compare
    
    	(loop for x in list
    		when (numberp x) collect x)

Now compare

(remove-if-not #'numberp list)

Seems to me this conversation is quickly reaching the point of
diminishing returns.  It is a feature an flaw of Lisp that there are
always at least n ways of doing the same thing...
From: Scott McKay
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <19920429172552.9.SWM@SUMMER.SCRC.Symbolics.COM>
    Date: Wed, 29 Apr 1992 10:16 EDT
    From: Hans Tallis <······@starbase.mitre.org>

    Sigh.  Let's review our basics:

    1. Code must be easy to read by the next person.
    Exercise for the reader:  write the loop equivalents for the comon mapping
    (mapcar, maplist, mapc, mapl, mapcan, mapcon) and iteration (do, dolist,
    dotimes) constructs and tell me which is easier to _read_.  For example,
    compare 
	    (setf list '(a b c)

	    (loop for x in list
		    collect (my-fcn x))

(mapcar #'my-fcn list) or (map 'list #'my-fcn list)

    and
	    (loop for x in list
		    do (my-fcn x)
		    return list)

(mapc #'my-fcn list) or use DOLIST.

    with the equivalent map forms (I can't figure out what they'd be with less than
    a few minutes study, but I think they're mapcar and mapc?)  Now try to argue
    that the latter are more readable to the new reader, and that errors in coding
    are more transparent with them.  Go ahead, I'm listening.  For my money, the
    difference between the above loop forms is more apparent than the mapc/mapcar
    typo will ever be.

To paraphrase you, "Try to argue that the LOOP forms are more readable,
and the errors in coding are more transparent.  Go ahead, I'm listening."

My point is, that you can't.  I find the MAP forms more readable.  Are
you going to say that I am *wrong* in finding them more readable?  This
is a stylistic point; both styles are fine.  I prefer the map functions,
you prefer LOOP; big deal, so what?

    Let's try another example:  how many people easily grok this example from
    CLtL2 (p. 172): 

	    (setf list '(a 1 b c 3 4 d 5))

	    (mapcan #'(lambda (x) (and (numberp x) (list x))) list)

(1 3 4 5).  It took me about 3 seconds to figure this out.  However, I
myself would generally use LOOP to do this.  Why?  Because LOOP provides
a collection facility not provided by any other part of Lisp.

As Barmar pointed out, as other collection facilities become used, using
LOOP or MAPCAN to do this may become less common.

    Quickly, what does this return?
	    - '((1) (3) (4) (5))
	    - '(a 1 b c 3 4 d 5)
	    - '(a b c d)
	    - '(1 3 4 5)
	    - '(nil 1 nil nil 3 4 nil 5)

    Now compare

	    (loop for x in list
		    when (numberp x)
		      collect x)

Again, the important thing here is that you are using LOOP's collection
facility.  But is anything wrong with this, which use a small collection
facility that I occasionally use.

  (with-collection ()
    (dolist (x list)
      (when (numberp x)
	(collect x))))

Well, it's a line longer, but I claim that it is no less clear.  If Lisp
provided such a collection facility, I would probably use it in place of
LOOP in this instance.  Would I be wrong in doing so?  I think not.

    Can this point be made any clearer?  It is no argument to say that map forms
    take fewer keystrokes to write.  Unless you've buried your head deep in the
    Lisp sandpile for several years I think you'll have to admit that the second
    form is easier to read.

I don't think anyone will claim that the LOOP form is clearer than the
MAPCAN form, but the people disagreeing with you have been quite
explicit that sometime the mapping and "doing" forms are better, and
sometimes they are not.  In this case, I agree that LOOP is better than
MAPCAN.

    2. Let me iterate this point: the dominant cost in software production is
    _maintenance_, at least for us writing large systems.  The time spent writing
    code generally makes up <= 10% of the system development time.  Ease of reading
    is _everything_ when writing code.  Despite claims that this is all syntactic
    sugar, anyone who's done serious software archeology will admit that healthy
    amounts of sugar can save lots and lots and lots of $$ in the long run.

Nobody has disagreed with you on this point.  The disagreement arise
when you baldly claim that LOOP is superior for everything, when that is
clearly not the case.

    3. Loop is very powerful, granted, and many people are trying to argue that
    "you can do so much with loop that it's unreadable."  
    This is not an argument.
    Loop, in fact, easily does lots of things that _none_ of the mapping and
    iteration forms do.  Personally, I'm no great loop hacker, and I never exercise
    most of the loop clauses available; generally I use the small subset of loop
    keywords necessary to implement the 9 forms listed above (< half a dozen), and
    rarely use _finally_ or _minimizing_ or what-have-you forms.  (Let's try to
    keep the discussion rational.)

The only "great thing" that you have used in the examples above is
LOOP's collection facility.  I don't think many people would disagree
LOOP's collection facility is useful.  But I hardly think that that is
enough evidence to dismiss the utility and readability of the other
iteration and mapping forms.
From: Mike Rose
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <MROSE.92Apr29133557@kali.stsci.edu>
>Let's try another example:  how many people easily grok this example from
>CLtL2 (p. 172): 
>	(setf list '(a 1 b c 3 4 d 5))
>	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)
>Quickly, what does this return?
>Now compare
>	(loop for x in list
>		when (numberp x) collect x)
>Can this point be made any clearer?  

Yes, it can.  On the same page Steele suggests remove-if-not:
    (remove-if-not #'numberp list)
I find this much easier to read than either of the above.
--
Mike Rose, ·····@stsci.edu, 410-338-4949
From: Hans Tallis
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tallis.704579166@starbase>
In <···················@kali.stsci.edu> ·····@kali.stsci.edu (Mike Rose) writes:
>>Can this point be made any clearer?  
>Yes, it can.  On the same page Steele suggests remove-if-not:
>    (remove-if-not #'numberp list)
>I find this much easier to read than either of the above.
>--
>Mike Rose, ·····@stsci.edu, 410-338-4949


Which brings me to another set of functions that should be discarded:  all
those _remove_ forms.  Thanks for the reminder.

My last post on this... (You're welcome.)  I like simple languages, and CL is
kinda big.  I see loop (with a subset of, oh, half a dozen of its keywords) as
providing a modular set of mapping/iteration constructs that can be used to
reimplement the combinatorial explosion of map'ing, do'ing, remove'ing, ad
nauseum forms.

For my sense of aesthetics, the former is a nicer way to build a language.

Perhaps loop's tend to look self-documenting, like COBOL, and thus don't seem
the choice of the macho lisp generation.  This hasn't bothered me yet.

For ease of readibility and and finding coding errors, I think the choice is
clear (for both sides arguing this point :-)).

For sake of inertia, I suppose one would choose the latter.

--Hans
From: Thomas A. Russ
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <21332@venera.isi.edu>
In article <...> ······@starbase.mitre.org (Hans Tallis) writes:

   For example, compare 

	   (setf list '(a b c)

	   (loop for x in list
		   collect (my-fcn x))
   and
	   (loop for x in list
		   do (my-fcn x)
		   return list)

   with the equivalent map forms...

The latter example doesn't do what you think it does.  It will call
MY-FCN on the first element of LIST, and then return LIST.  To be
equivalent to mapc, it needs to be:

	   (loop for x in list
		   do (my-fcn x)
		   FINALLY (return list))

with the return in parentheses!

--

Thomas A. Russ                                             ···@isi.edu    
USC/ISI, 4676 Admiralty Way, Marina del Rey, CA 90292      (310) 822-1511
From: Denys Duchier
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <DDUCHIER.92Apr29165038@kaml1.csi.uottawa.ca>
Hans Tallis writes:
 > 	(loop for x in list
 > 		when (numberp x) collect x)
 > 
 > Can this point be made any clearer?  It is no argument to say that map forms
 > take fewer keystrokes to write.  Unless you've buried your head deep in the
 > Lisp sandpile for several years I think you'll have to admit that the second
 > form is easier to read.

My sandpile says:

	(remove-if-not #'numberp list)

:-)

--Denys
From: Richard Shapiro
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <1992Apr30.140104.4696@arris.com>
In article <················@starbase> ······@starbase.mitre.org (Hans Tallis) writes:
>Sigh.  Let's review our basics:
>
>1. Code must be easy to read by the next person.

By the next competent programmer, you mean. Lisp programmers are
generally quite comfortable with the various map functions and find
code that uses them extremely clear - at least as clear as loop code.



>	(setf list '(a b c)
>
>	(loop for x in list
>		collect (my-fcn x))

Unquestionably, (mapcar #'my-fcn list) is a cleaner, clearer way to
write this. As an experienced lisp programmer, I understand instantly
what the mapcar version is doing. I have to *parse* the loop to
understand it.


>	(loop for x in list
>		do (my-fcn x)
>		return list)

Ditto, (mapc #'my-fcn list). Same argument as above.

>that the latter are more readable to the new reader, and that errors in coding
>are more transparent with them.

It doesn't really make sense to use "the new reader" as the intended
audience. Even if it did, a new reader who was raised on Lisp would
prefer the map versions, I'm quite sure. Only a cross-over new reader,
who wanted Lisp to look more conventionally procedural (ala Pascal or
C or whatever) would prefer the loop versions. Lisp is a powerful
language, but not if you insist on pretending it's just Pascal with
parens.


>Let's try another example:  how many people easily grok this example from
>CLtL2 (p. 172): 
>
>	(setf list '(a 1 b c 3 4 d 5))
>
>	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)

This is ugly, true, but that's because it's a silly way to write it.
An immediately understandable form (again, to an experienced lisp
programmer) would be:

  (remove-if-not #'numberp list).

Seems clear to me: remove anything from list which is not a number.
The loop version is very clunky and awkward by comparison, though I'll
grant you it's not as awkward as the mapcan above.


>2. Let me iterate this point: the dominant cost in software production is
>_maintenance_, at least for us writing large systems.


Of course, but maintenance by programmers who are comfortable with the
constructs of the language they're maintaining. If you give a C
programmer the job of maintaining a Lisp program, *of course* he or
she will be confused by the more functional nature of Lisp (among
other things). So what?

>Loop, in fact, easily does lots of things that _none_ of the mapping and
>iteration forms do.

Yes, loop has a place in the language for some purposes. So do the map
functions and the various sequence functions (like remove -- see
above) that inexperienced lisp programmers often replace with ugly
loop code.
From: Jeff Dalton
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <6636@skye.ed.ac.uk>
There are some cases where LOOP is (IMHO) clearly better than the
alternatives.  Most messages so far have compared LOOP with mapping
functions, but those are not the cases where I think things look best
for LOOP.  Compare instead with DO (expecially for numeric iteration)
and DOTIMES.

BTW, DOTIMES is surprisingly losing for the one thing it does.
If you want fixnum arithmetic you typically have to code a DO
by hand rather than use DOTIMES.

-- jeff
From: Barry Margolin
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tsfugINN163@early-bird.think.com>
In article <····@skye.ed.ac.uk> ····@aiai.ed.ac.uk (Jeff Dalton) writes:
>BTW, DOTIMES is surprisingly losing for the one thing it does.
>If you want fixnum arithmetic you typically have to code a DO
>by hand rather than use DOTIMES.

CMU CL generates fixnum arithmetic for the following:

(defun foo (x)
  (declare (fixnum x))
  (dotimes (i x)
    ...))

So long as the limit of the iteration is a fixnum, the iteration variable
can be determined to be a fixnum.  It takes a reasonable type inferencing
system to determine this, but we've know for years that type inference is
important if you don't want to have to litter your program with
declarations.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Marty Hall
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <1992Apr30.165510.1230@aplcen.apl.jhu.edu>
Keywords: 

In article <················@starbase> ······@starbase.mitre.org 
(Hans Tallis) writes:
>
>1. Code must be easy to read by the next person.
>Exercise for the reader:  write the loop equivalents for the comon mapping
>(mapcar, maplist, mapc, mapl, mapcan, mapcon) and iteration (do, dolist,
>dotimes) constructs and tell me which is easier to _read_.  For example,
>compare 
>	(setf list '(a b c)
>
>	(loop for x in list
>		collect (my-fcn x))

Well, I'd say (mapcar #'my-fcn list) is no harder to read for me. I think
this is mostly a matter of what you are used to, and can somewhat fall into
the "religious" category. For instance, one of Hans' arguments against 
dolist/dotimes was that it was hard to remember which was the local variable,
the return form, etc. This is purely a matter of familiarity, IMHO. I used
to always forget whether the List or the Index came first in "nth". Does that
mean "nth" is unreadable? However, I do agree that many places where one
used doxxxx (I *never* used "do") can be written at least as easily using
loop. Mapping is another matter, I think. One reason I sometimes prefer 
mapcar is that you can cast functions into a functional programming style, 
with nested function calls (mapcar is a real function), rather than a more 
imperative style. In some contexts, this yields code that, to me, seems more
readable and easy to maintain. For example, a function to take two points in 
N-space (N >= 1) and find the Euclidean Distance between them. Points are 
just lists of coordinates, and of course the points need to be of the same 
dimensionality.

(defun Euclidean-Distance (Point-1 Point-2)
  (sqrt (apply #'+ (mapcar #'Square (mapcar #'- Point-1 Point-2)))) )

(defun Square (X) (* X X))

Clearly the two mapcar forms could be written as loop...collecting forms,
but I'm not sure it would be more readable (certainly not to me), and you
would lose some of the functional-programming feel that I think is beneficial
here.

This in no way is meant to argue that mapcar is better than loop, just to
argue against Hans' claim that loop is *always* preferable to any of the
mapping forms. Hans had other examples that I did not include, and I have
no doubt loop is the right choice in several of them. Picking the best tool
for the job is the key, from my perspective.

				- Marty
------------------------------------------------------
····@aplcen.apl.jhu.edu, ···········@jhunix.bitnet, ..uunet!aplcen!hall
Artificial Intelligence Lab, AAI Corp, PO Box 126, Hunt Valley, MD 21030

(setf (need-p 'disclaimer) NIL)
From: Barry Margolin
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tp8qdINNuq@early-bird.think.com>
In article <················@starbase> ······@starbase.mitre.org (Hans Tallis) writes:
>compare 
>	(loop for x in list
>		collect (my-fcn x))
>and
>	(loop for x in list
>		do (my-fcn x)
>		return list)

Assuming the second one is supposed to be equivalent to (mapc #'my-fcn
list), it has a bug: you forgot to say "finally" before the "return".  If I
were using LOOP instead of MAPC I'd almost always punt the return value;
I've never actually seen anyone use the return value of MAPC.

>with the equivalent map forms (I can't figure out what they'd be with less than
>a few minutes study, but I think they're mapcar and mapc?)  Now try to argue
>that the latter are more readable to the new reader, and that errors in coding
>are more transparent with them.  Go ahead, I'm listening.  For my money, the
>difference between the above loop forms is more apparent than the mapc/mapcar
>typo will ever be.

As I've already mentioned, the names of the mapping functions are horrible.
Are you arguing against mapping functions in general, or just the current
names?  If MAPC were named MAP-LIST-ELEMENTS and MAPCAR were named
MAP-LIST-ELEMENTS-RETURNING-RESULTS, would you still prefer LOOP over the
mapping functions?

And what about

	(dolist (x list)
	  (my-fcn x))

An advantage of this over the equivalent LOOP is that accidentally hitting
"o" instead of "i" (which are adjacent on most keyboards, and thus an easy
typo) doesn't result in a different, but still valid, form; compare this
with:

	(loop for x on list
	      do (my-fcn x))

It has only one character different from the correct form, but will have
very different effects.  The only two mapping functions that differ by only
a letter are MAPCON and MAPCAN; they're rarely used, and harder to typo.

>	(mapcan #'(lambda (x) (and (numberp x) (list x))) list)

>	(loop for x in list
>		when (numberp x) collect x)

I agree with this example.  Once I learned LOOP I never used the above
idiom again.  I also never use

(do ((result nil)
     ...)
    (<test> (nreverse result))
  ...
  (push <something> result)
  ...)

Which was another common pre-LOOP idiom for collecting things into a list
during an iteration.

>Can this point be made any clearer?  It is no argument to say that map forms
>take fewer keystrokes to write.  Unless you've buried your head deep in the
>Lisp sandpile for several years I think you'll have to admit that the second
>form is easier to read.

I don't think a single message in this discussion has suggested that
keystroke savings was a reason to use CAR/CDR instead of FIRST/REST, or to
use mapping functions or do macros rather than LOOP.  Most of the arguments
have been that there are appropriate times for all of these things, and
it's not appropriate to dismiss some functions completely.

>2. Let me iterate this point: the dominant cost in software production is
>_maintenance_, at least for us writing large systems.  The time spent writing
>code generally makes up <= 10% of the system development time.  Ease of reading
>is _everything_ when writing code.  Despite claims that this is all syntactic
>sugar, anyone who's done serious software archeology will admit that healthy
>amounts of sugar can save lots and lots and lots of $$ in the long run.

One of Lisp's strong features is the ability to program at appropriate
levels of abstraction.  Conciseness isn't just a matter of saving
keystrokes.  So long as it isn't taken to an obscene degree (like C and
APL), it can enhance understandability.  Every token in a form is something
the reader has to look at and determine why it's there; the more there are,
the more opportunity for misunderstanding.  Lisp allows us to consolidate
common concepts into simple notations, which can be recognized easily.  

As readable as many LOOP forms are, you have to admit that they're at a
slightly lower level of abstraction than most of the alternatives.  For
instance, the programmer is required to provide a name for the iteration
variable, when all he cares about is that the iteration object be passed to
the iteration function somehow; the mapping functions and Waters's Series
package make this argument passing implicit.

I admit that the abstraction level between LOOP and the mapping functions
are pretty close.  However, in one of the messages in this thread someone
(was it you?) suggested that LOOP be used instead of the sequence functions
such as REMOVE.  The sequence functions automatically work on both lists
and vectors (I thought X3J13 had added a LOOP iterator that does this, but
I was wrong -- all we added was the ACROSS iterator, which works on
vectors).  They don't require you to examine the logic of the loop body to
determine how the result is derived from the input -- the name REMOVE is
perfectly obvious (although the distinction between REMOVE and DELETE
simply has to be memorized -- the Scheme folks' use of a standard suffix is
much nicer, and a Common Lispy way to do it might have been to add a
:DESTRUCTIVE keyword argument, but we're stuck with lots of MacLisp
mistakes).  My only real complaint about the sequence functions is that
there's no KEEP-IF function; almost all my uses of REMOVE-IF-NOT (which is
deprecated in ANSI CL, in favor of using REMOVE-IF and COMPLEMENT) are for
this purpose, and I think it would be much easier to read if there were
such a function.

>3. Loop is very powerful, granted, and many people are trying to argue that
>"you can do so much with loop that it's unreadable."  
>This is not an argument.

But it is!  Because any use of LOOP has the potential to be unreadable, the
reader must read it carefully to verify that it's just one of the cases
that doesn't require careful reading!

I often make a similar point about the difference between LET and LET*.
Some programmers have said that they always use LET*, in case they later
need to add a binding that's dependent on an earlier one.  But whenever I
see a LET* form, the asterisk alerts me to the fact that I should look
through the bindings to find the dependencies.  If there aren't any, I've
just wasted my time looking for them, and I'm also left wondering whether
the programmer used the wrong variable in one of the initial value forms.

>Loop, in fact, easily does lots of things that _none_ of the mapping and
>iteration forms do.  Personally, I'm no great loop hacker, and I never exercise
>most of the loop clauses available; generally I use the small subset of loop
>keywords necessary to implement the 9 forms listed above (< half a dozen), and
>rarely use _finally_ or _minimizing_ or what-have-you forms.  (Let's try to
>keep the discussion rational.)

If most people only wrote such simple loops, there would be no need for
LOOP.  It's much better to have special-case forms that indicate precisely
what you're doing.  Assuming the forms have reasonable names, this makes it
easier for a reader to understand them.  And whatever the names are, it
makes it easier for the compiler to generate good code for them.  I would
never use LOOP when DOLIST or DOTIMES would do.  The general forms like
LOOP are for times when the simple forms aren't appropriate.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Emmanuel Baechler
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <BAECHLER.92Apr29173434@liasun6.epfl.ch>
In article <······················@aero.org> ········@Aero.org (David Schulenburg) writes:

>  WRONG!  loop is one of the major abominations in common lisp, what with all
>  of its reserved words.  how are the following "instantly readable"? [...]

That's pretty true. I remember to have seen very long, complex and confusing
discussions about some aspects of loop on the net. Personally I consider that
it was a real mistake to include this monster in the Common Lisp standard. I
much perfer the smaller and simpler macro in the Charniak (AI Programming
2e edition). It is simple, small and uses real keywords instead of symbols.
You can only find in it what has to be placed in an iteration macro instead
of a collection of all the features that one can ever imagine. IMHO, that's
a much better policy.

About the original topic: that's true that one can get rid of mapping
functions and use an explicit iteration. But, if you go this way, why
don't you directly go to assembly language? That's the logic consequence
of this policy.

The interest of mapping functions is that it allows you to express things
in a way which correspond to what you conceptually want to do, (i.e. apply
an operator to a set or a sequence of arguments and collect the result in
some way) instead of the way it has to be compiled in order to be calculated.
IMHO, the closer your code is to what you want to do, instead of the way it
has to be expressed to be calculated, the better it is. The rest is a matter
of macros and optimization.

--
Emmanuel Baechler.                        | Tel.: ++41-21-693-2732
Laboratoire d'Intelligence Artificielle   | e-mail: ········@liasun6.epfl.ch
Ecole Polytechnique Federale de Lausanne  | Standard Disclaimer
MA-Ecublens
CH-1015 Lausanne	Switzerland

Ban the bomb. Save the world for conventional warfare.
From: Barry Margolin
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tn4reINNkgl@early-bird.think.com>
In article <······················@liasun6.epfl.ch> ········@lia.di.epfl.ch (Emmanuel Baechler) writes:
>Personally I consider that
>it was a real mistake to include [LOOP] in the Common Lisp standard. I
>much perfer the smaller and simpler macro in the Charniak (AI Programming
>2e edition). It is simple, small and uses real keywords instead of symbols.

Since one of the primary purposes of a standard is to codify existing
practice, and LOOP is implemented by most CL vendors and source code for it
was frequently requested on the net, it makes good sense to include it in
the standard.  It wasn't put in the standard as an endorsement (many of the
X3J13 members who voted for its inclusion don't care for it as a
programming style), but as a way to rope in the diverging implementations
so that the LOOP code that people write could be portable.  People have
been using LOOP for years without it being in the standard, so there's no
reason to think that they're going to stop any time soon; the best we can
hope for is that they're all using the same LOOP.  I never saw any similar
demand by users or vendors for any other iteration system; we just gave the
Lisp community what it wanted.

I'm not familiar with Charniak's interation macro, but the consensus of the
committee was that the "right" ways to do iteration in Lisp are in the
functional styles of Waters's Series package and the Generators/Gatherers
macros.  They weren't included in the standard because we didn't feel
they'd had enough exposure to require all implementors to provide them;
Steele included in them in CLtL2 as a way to encourage vendors to provide
them as extensions and users to give them a try.  Generators/Gatherers, in
particular, is dependent on good optimization of downward closures to get
the kind of performance that LOOP can get.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Emmanuel Baechler
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <3699@disuns2.epfl.ch>
In article <···········@early-bird.think.com>, ······@think.com (Barry Margolin) writes:

|> the best we can hope for is that they're all using the same LOOP.

I won't and the two other followups of people who strongly dislike the current
LOOP macro are a sign that I am probably not alone.

|> I'm not familiar with Charniak's interation macro, but the consensus of the
|> committee was that the "right" ways to do iteration in Lisp are in the
|> functional styles of Waters's Series package and the Generators/Gatherers
|> macros. 

Series, generators and gatherers are IMHO a much better idea than the Loop
that was included in the standard. I am sure that their exposure in CLTL II
and in an issue of ACM Lisp Pointers will make them very popular.


-- 
Emmanuel Baechler.                        | Tel.: ++41-21-693-2732
Laboratoire d'Intelligence Artificielle   | e-mail: ········@liasun6.epfl.ch
Ecole Polytechnique Federale de Lausanne  | Standard Disclaimer
MA-Ecublens
CH-1015 Lausanne	Switzerland

Ban the bomb. Save the world for conventional warfare.
From: lawrence.g.mayka
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <LGM.92Apr30095407@cbnewsc.ATT.COM>
In article <····@disuns2.epfl.ch> ········@liasun5.epfl.ch (Emmanuel Baechler) writes:

   In article <···········@early-bird.think.com>, ······@think.com (Barry Margolin) writes:
   |> I'm not familiar with Charniak's interation macro, but the consensus of the
   |> committee was that the "right" ways to do iteration in Lisp are in the
   |> functional styles of Waters's Series package and the Generators/Gatherers
   |> macros. 

   Series, generators and gatherers are IMHO a much better idea than the Loop
   that was included in the standard. I am sure that their exposure in CLTL II
   and in an issue of ACM Lisp Pointers will make them very popular.

I, too, think that Dick Waters' Series combine the clean, functional
semantics of mapping with the flexibility and optimizability of LOOP.
As yet, though, I don't know of any Common Lisp vendor providing
support for Series or integrating it into their product.  Dick Waters'
portable implementation is, of course, available for anonymous FTP.

	Lawrence G. Mayka
	AT&T Bell Laboratories
	···@iexist.att.com

Standard disclaimer.
From: Barry Margolin
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <tqk5mINN1c0@early-bird.think.com>
In article <····@disuns2.epfl.ch> ········@liasun5.epfl.ch (Emmanuel Baechler) writes:
>In article <···········@early-bird.think.com>, ······@think.com (Barry Margolin) writes:
>|> the best we can hope for is that they're all using the same LOOP.
>
>I won't and the two other followups of people who strongly dislike the current
>LOOP macro are a sign that I am probably not alone.

What does the "I won't" refer to?  You won't hope that people who are use
LOOP use the same definition?  I fail to see why you would want LOOP
specifications to diverge?  Do you think that would stop people from using
it?  LOOP hasn't been standardized for years, and there are divergent
implementations, yet people continue to use it.  And some people in this
discussion apparently think that it's the best thing since sliced bread.

-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Emmanuel Baechler
Subject: Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design")
Date: 
Message-ID: <BAECHLER.92May1130910@liasun6.epfl.ch>
>>> the best we can hope for is that they're all using the same LOOP.
>>
>> I won't and the two other followups of people who strongly dislike
>> the current LOOP macro are a sign that I am probably not alone.

> What does the "I won't" refer to?  You won't hope that people who are use
> LOOP use the same definition?  I fail to see why you would want LOOP
> specifications to diverge?

I won't use the standard LOOP construct, as defined in the Cltl II, because
it doesn't have the syntax that I want and because I think that a loop
construct which require 40 pages of description doesn't make sense. In
addition, I hate to see symbols used as keywords, I want to be able to
keep them as variables.

I don't want LOOP specifications to diverge, but, Lisp is a tool for me
that I want adapted to myslef and my needs, not the contrary. As for 
every other feature, I want a loop construct that does what I want the 
way I want and with an accpetable syntax for me. 

That's one of the real advantage of LISP that, when an existing mechanism
doesn't please you (or isn't adapted to your need), you have the ability 
to build your own alternative, even if it causes a divergence. As long as
I can use my own Loop construct (in fact the one of the Charniak), the
existance of a huge, ugly and much too complicated standard construct is
not a big problem. I still hope however that something else will be adopted
some day.

--
Emmanuel Baechler.                        | Tel.: ++41-21-693-2732
Laboratoire d'Intelligence Artificielle   | e-mail: ········@liasun6.epfl.ch
Ecole Polytechnique Federale de Lausanne  | Standard Disclaimer
MA-Ecublens
CH-1015 Lausanne	Switzerland

Ban the bomb. Save the world for conventional warfare.
From: Barry Margolin
Subject: Iteration mechanisms (was Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design"))
Date: 
Message-ID: <kvrri6INNf0p@early-bird.think.com>
In article <················@starbase> ······@starbase.mitre.org (Hans Tallis) writes:
>Amen.  I deem it unacceptable to use any mapping functions whatsoever.  Ditto
>for all _do_ forms.  They are tough to write and impossible to read.  There's
>no excuse for not using a loop macro, which is instantly readable by anyone.

I think they all have their appropriate uses.

The mapping functions are useful when all you want to do is apply an
existing function to every element of a list.

(mapcar #'cons list1 list2)

(mapc #'print objects)

are quite simple to write *and read*.

I don't find too much use for the original DO these days, but DOLIST and
DOTIMES are perfectly fine.  DOLIST is nice for things that would have
required a lambda expression when using MAPC.  For instance, I find

(dolist (item list)
  (print (car item)))

easier to read and write than

(mapc #'(lambda (item) (print (car item))) list)

Finally, I use LOOP when I'm doing something that can make use of its
automatic result mechanisms (usually COLLECTING or SUMMING) or I'm using a
combination of multiple simultaneous iteration paths.  I prefer

(loop for i upfrom 0
      for item in list
  ...)

to

(do ((i 0 (1+ i))
     (tail list (cdr tail))
     item)
    ((null tail) ...)
  (setq item (car tail))
  ...)

When generators and gatherers become more widely implemented I think they
will usually be useful where LOOP currently is.

In article <······················@aero.org>, ········@Aero.org (David Schulenburg) writes:
>WRONG!  loop is one of the major abominations in common lisp, what with all
>of its reserved words.

Well, I generally find LOOP pretty readable, but I admit that LOOP has many
details that can be used to make it as incomprehensible as anything else
(this is a corollary to the statement that it's possible to write poor
programs in any language).  The AND connective between iteration clauses is
relatively uncommon.  The mechanism for multiple collectors is a bit
tricky.  Whether certain exit mechanisms execute the FINALLY clause can be
confusing.  And I usually have to look up what the return value of a loop
using the THEREIS or ALWAYS collector is (however, I don't understand why
Mr. Schulenburg has trouble understanding the MAXIMIZING or SUMMING
collectors).

Note, however, that the keywords in LOOP aren't really "reserved".  They
can be used as variable names, since it's always possible to distinguish
variables from keywords by context.  The following is valid and unambiguous
(but that doesn't make it good style):

(deftype of-type ...)
(defvar = ...)
(defvar in ...)
(defvar from ...)
(defvar to ...)
(defvar by #'...)
(defvar collecting ...)

(loop with with of-type of-type = =
      for for in in
      and and from from to to by by
      collecting collecting)
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Josh Fisher
Subject: Re: Iteration mechanisms (was Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design"))
Date: 
Message-ID: <1992Apr29.164131.7822@hplabsz.hpl.hp.com>
In article <············@early-bird.think.com> ······@think.com (Barry Margolin) writes:
            . . .

>In article <······················@aero.org>, ········@Aero.org (David Schulenburg) writes:
>>WRONG!  loop is one of the major abominations in common lisp, what with all
>>of its reserved words.
>
>Well, I generally find LOOP pretty readable, but I admit that LOOP has many
>details that can be used to make it as incomprehensible as anything else

            . . .

Jonathan Amsterdam's "Iterate" is a powerful superset of LOOP.  One
nice side effect of using it is that you can use the clauses LOOP
uses, but with syntax that looks like lisp (i.e.  parens where you'd
like them). It has lots of other features/advantages.  I find that I
almost always use it to iterate, and like it a lot.  It's FTPable from
MIT.

						-- Josh Fisher
						   HP Labs
From: R. Bharat Rao
Subject: Re: Iteration mechanisms (was Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design"))
Date: 
Message-ID: <1992Apr29.180519.670@m.cs.uiuc.edu>
······@think.com (Barry Margolin) writes:

>In article <······················@aero.org>, ········@Aero.org (David Schulenburg) writes:
>>WRONG!  loop is one of the major abominations in common lisp, what with all
>>of its reserved words.

>Note, however, that the keywords in LOOP aren't really "reserved".  They
>can be used as variable names, since it's always possible to distinguish
>variables from keywords by context.  The following is valid and unambiguous
>(but that doesn't make it good style):

I think LOOP is an improvement over the sundry map's, and I have been
using a FOR macro (very similar to LOOP but with :keywords) for 5
years at this point.  My main beef with the LOOP macro is the use of
symbols as keywords...

I think "they" made a big mistake when they decided to use plain ole
symbols as keywords instead of :keywords.

IMHO, it would be much better style and more readable to say

(loop :FOR x :IN '(a b c 1 2 3) :when (numberp x) :COLLECT x)

        than the current....

(loop FOR x IN '(a b c 1 2 3) when (numberp x) COLLECT x)

There would then be no problems in confusing variables with keywords.

Bharat
--
R. Bharat Rao                          E-mail: ······@cs.uiuc.edu 
AI Group, Beckman Institute for Advanced Science and Technology
405 N. Matthews, Urbana, IL 61801  (217)-244-1263 (O) 337-6498(H)
Electrical & Computer Engineering, University of Illinois, Urbana
From: lawrence.g.mayka
Subject: Re: Iteration mechanisms (was Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design"))
Date: 
Message-ID: <LGM.92Apr30094110@cbnewsc.ATT.COM>
In article <····················@m.cs.uiuc.edu> ······@cs.uiuc.edu (R. Bharat Rao) writes:
   years at this point.  My main beef with the LOOP macro is the use of
   symbols as keywords...

   I think "they" made a big mistake when they decided to use plain ole
   symbols as keywords instead of :keywords.

   IMHO, it would be much better style and more readable to say

   (loop :FOR x :IN '(a b c 1 2 3) :when (numberp x) :COLLECT x)

	   than the current....

   (loop FOR x IN '(a b c 1 2 3) when (numberp x) COLLECT x)

   There would then be no problems in confusing variables with keywords.

CLtL/II says that LOOP "keywords" are recognized by print name,
regardless of package; hence, the use of true keywords in LOOP is
quite permissible.  I myself used that style for a long time.  Its
main disadvantage is that tools such as indenters may not recognize
the constructs properly.

	Lawrence G. Mayka
	AT&T Bell Laboratories
	···@iexist.att.com

Standard disclaimer.
From: Barry Margolin
Subject: Re: Iteration mechanisms (was Re: CAR/CDR vs FIRST/REST (was Re: Book review: "Lisp Style and Design"))
Date: 
Message-ID: <tpa4tINN45c@early-bird.think.com>
In article <····················@m.cs.uiuc.edu> ······@cs.uiuc.edu writes:
>I think "they" made a big mistake when they decided to use plain ole
>symbols as keywords instead of :keywords.

:Keywords didn't exist when LOOP was invented, so there was no decision to
make (although since the colon was just an ordinary symbol constituent in
those days, if they'd had precognition they could have used it as the first
character of all the loop keywords' names).

>IMHO, it would be much better style and more readable to say
>
>(loop :FOR x :IN '(a b c 1 2 3) :when (numberp x) :COLLECT x)
>
>        than the current....
>
>(loop FOR x IN '(a b c 1 2 3) when (numberp x) COLLECT x)

I don't see what readability improvement there is over upcasing the
keywords.  They both make the keywords stand out.  Then again, I've never
had trouble understanding individual LOOP clauses, even without making the
keywords stand out; as with the rest of Lisp, good indentation often helps.

By the way, since loop keywords are recognized by their name, not identity,
you can use :keywords if you think it would help your readers.  If it's
really helpful, maybe it will catch on and become popular.  Personally, I
don't like it; LOOP forms are supposed to read like English, and to me the
colons are jarring interruptions in the flow.

>There would then be no problems in confusing variables with keywords.

I suppose it would allow for slightly better error messages in case you
leave out or double a keyword, e.g.

	(loop for for x in list do (print x))

Other than this, so long as no one actually writes real code like the (loop
for for in in ...) example I posted, I don't see any problem with confusing
variables and keywords.

-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Bob Riemenschneider
Subject: Re: Book review: "Lisp Style and Design"
Date: 
Message-ID: <RAR.92Apr27153406@birch.csl.sri.com>
In article <····@disuns2.epfl.ch> ·····@litsuns1.epfl.ch (Marc Riese) writes:

   I think that, aside from the uses Norvig mentions, car and cdr are "bad"
   because their names are obscure and therefore they diminish sw readability.
   Good software should be written with its future readers in mind.

While I agree that using FIRST/REST for list accessors and CAR/CDR for
pair accessors only -- an idea that predates including FIRST and REST in
the language definition, by the way (see, e.g., Allen's _Anatomy of
LISP_) -- makes a lot of sense, anyone who finds that "old-fashioned"
uses of CAR and CDR significantly diminishes readability has no business
reading Lisp!

							-- rar
From: Jeff Dalton
Subject: Re: Book review: "Lisp Style and Design"
Date: 
Message-ID: <6635@skye.ed.ac.uk>
In article <····@disuns2.epfl.ch> ·····@litsuns1.epfl.ch (Marc Riese) writes:
>I wrote:
>>g. A Lisp style book espousing readability should imitate Winston and 
>>   encourage use of "first" and "rest" when discussing lists, and leave
>>   mention of "car" and "cadr" for the folklore section. The authors present
>>   the latter operators first. (p32,42)

If W&H think car are cdr are just parts of Lisp folklore, they are
wrong.  They are still used much more than first and rest, and they
are still used by most of the best Lisp programmers.

What's happened is that some people are embarrassed by car and cdr,
in part because they are so often used by anti-Lisp opportunists to
attack Lisp, and so those people would _like_ to have car and cdr
replaced.  They'd like it to happen, but it hasn't.

We have to ask why is it that Lisp programmer still use car and cadr?
Indeed, why do they put up with Lisp at all?  The horrible syntax!
All those parens!  We ought to consider that Lisp programmers might
have good reasons for doing these things and that the way things are
done in Lisp might even have advantages.  Many people who don't like
Lisp will not accept this; they think the reasons have to be _bad_
reasons.  They think it must that Lisp programmers use car and cdr
because that's what they're used to, because they don't know any
better, or because they're sloppy hackers uninterested in the quality
of their code.

But those of us who _do_ like Lisp should not be so quick to conclude
that current practice in this case is stupid.

>Jeff Dalton writes:
>>FIRST and REST are right for some cases, wrong for others.
>>And what's so bad about car and cdr anyway?
>
>In "Lisp" 3rd Ed. page 21, Winston states
>  "FIRST and REST were originally called CAR and CDR because of the way
>   they were implemented on the ancient IBM 704 computer."

So?  We're supposed to reject names because of their _origin_?

Even today, many people claim that, eg, "car" _means_ "contents
of address register".  That is completely the wrong way to think 
of it.  "Contents of address register" is part of the etymology
of "car", but not what "car" means as an element of Lisp vocabulary.

There are only two reasonable arguments against car and cdr.

1. The names do not make their meaning clear.  "First" and "rest"
   are supposedly better on these grounds.

Response: There's nothing wrong with learning some new technical
vocabulary when learning a language, and there's nothing that says
all new technical terms have to be new definitions for existing
English words.  

2. Car and cdr are used for two conceptually different things:
   parts of lists and parts of pairs (cons cells).

Response: If you think it's important to make it clear which you're
doing, use car and cdr for pairs and first and rest for lists.
But in most cases it's very unlikely that any confusion would
result.  The idea what we should always make a distinction might
appeal to purists, but we don't all have to be purists.

>On page 104, Winston states:
>  "Mapcar is perhaps the most unfortunately-named primitive in Lisp.
>   The map part is alright ... The car part, however, comes from the
>   now-obsolete CAR primitive ... MAP-ELEMENTS would have been a better name."

Again, the claim that car is "now obsolete" is just wrong.
W&H might wish it were obsolte (ie, gone out of use), and they
might hope their book will help to make it become obsolete.
But it is not, in fact, obsolete.

Note, moreover, that MAP-FIRST is completely losing.  (So much for
the idea what we can simply replace CAR by FIRST wherever it occurs.)

>In "AI Programming" page 14, Norvig states:
>  "I'm sure you'll agree that first and rest are much better names, and they
>   will be used whenever we are talking about lists. However, we will continue
>   to use car and cdr on occasion when we are considering a pair of values
>   that are not considered as a list."

You have to expect the authors of textbooks to argue for what they
think best practice ought to be.  But they can be wrong, and I would
rather trust the Lisp community to determine for itself what works
best in practice.  This is, of course, a conservative argument, and
I don't expect it to appeal to those inspired by a radical zeal
for reform.

>I think that, aside from the uses Norvig mentions, car and cdr are "bad"
>because their names are obscure and therefore they diminish sw readability.
>Good software should be written with its future readers in mind.
>
>This was an annoying, but not very important, aspect of a useful book.

Why is it annoying?  Why did you wrote "aside from the uses Norvig
mentions" if you didn't agree with him about those uses?

I do not agree that car and cdr diminish readability in any
significant way nor with your implicit claim that readability
is a sufficient reason in itself.

-- jeff