CML ITERATION PROPOSAL (Kelly Roach)

     The consensus as I read it is to adopt a lispy LOOP.  No decision came out of the committee apparently because no one wanted to be chair and some lack of interest in changing the "status quo" (i.e. LOOP).  People were arguing that LOOP should be made a little bit less English-keyword-like and a little more parenthesized-keyword-like, which is kind of surprising to me since that is sort of the way I think it ought to be but wouldn't expect anyone else to buy it.  PSL--which I had never heard of before, somewhat like LOOP but the parentheses already correct--was brought up by one person.  I myself like LOOP both the way it is and as proposed with parentheses.  LOOP grew out of Interlisp's own Clisp iterative constructions, so remerging the two most widespread iterative constructions of this type ought to fit in with Common Lisp philosophy.

                                PROPOSAL

     I propose Common Lisp adopt the lispy LOOP.  The best reference to look at is the documentation of LOOP in the revised MACLISP manual.  Syntax:

        (LOOP (keyword1 ...)
              (keyword2 ...)
              ...
              (keywordN ...))

Each "keyword" is one of the atoms FROM, FOR, DO, IN, ON, COLLECT, NCONC, APPEND, SUM, COUNT, ALWAYS, NEVER, THEREIS, WHEN, UNLESS, UNTIL, INITIALLY, FINALLY, MAXIMIZE, and MINIMIZE.  DECLARE:, LET, LET*, and user invented keywords are doable but more controversial/problematical.  LOOP arithmetic is mixed unless declarations are sufficient for LOOP to use integer of floatp arithmetic.  The syntax for the iteration clauses would be as follows:

    DRIVING CLAUSES.  Normally terminate by jumping to epilog code.      
(FROM I n), (FROM I n m), (FROM I n m k)
-- for I from n
-- for I from n to m
-- for I from n to m by k
(FOR I e), (FOR I e f)
-- for I ← e
-- for I ← e by f
(DO e)
-- do e
(IN V e)
-- for V in e
(ON V e)
-- for V on e

    PROLOG & EPILOG.
(INITIALLY e)
-- Adds form e to LOOP's prolog code.  After local bindings but before beginning to iterate, eval e.
(FINALLY e)
-- Adds form e to LOOP's epilog code.  After normal termination of driving clauses or termination by terminating clauses, eval e.

    VALUE CLAUSES.  If a variable V is supplied, then values are accumulated into the LOOP variable V.  Other clauses (e.g. a FINALLY clause containing (RETURN V)) are responsible for seeing that the value of V be used.  If no V is supplied then accumulate anonymously, value returned when driving clauses finish.  V is local to the LOOP and is initially NIL, 0, or etc.
(COLLECT e), (COLLECT e V)
-- collect e
(NCONC e), (NCONC e V)
-- join e
(APPEND e), (APPEND e V)
-- append values of e
(SUM e), (SUM e V)
-- sum e
(COUNT), (COUNT V)
-- add 1
(MAXIMIZE e), (MAXIMIZE e V)
-- largest e
(MINIMIZE e), (MINIMIZE e V)
-- smallest e

     BOOLEAN CLAUSES.  Abnormally terminate by returning without jumping to epilog code.
(ALWAYS e), (NEVER e), (THEREIS e)
-- always e, never e, thereis e;
Skip epilog code if any of these terminate the LOOP, returning NIL, NIL, value of e resp.  In normal termination, epilog code is jumped to and return T, T, NIL resp.

     CONDITIONALIZING CLAUSES.
(WHEN e), (UNLESS e)
-- For WHEN, if e evals to NIL this pass, skip following clause this pass.  UNLESS is similar.

     TERMINATING CLAUSES.  Normally terminate by jumping to epilog code.
(WHILE e), (UNTIL e)
-- For WHILE, if e evals to NIL, jump to epilog code.  UNTIL is similar.  Evaluating a RETURN expression inside a LOOP terminates without the epilog.

     LET, LET*, DECLARE, user keywords.
(LET ...), (LET* ...) 
-- Common Lisp equivalents of Interlisp's bind.  The "..."'s can be filled in exactly the same way you would fill in the bindings for a LET or LET* form.  There is however no LET/LET* body after the "..."'s.
(DECLARE ...)
-- This DECLARE follows the same syntax as other Common Lisp DECLARE's.  See Chapter 9 and especially section 9.2.  I suppose we are using CL:DECLARE for the time being.  Variables introduced by LET, LET*, FOR, FROM, SUM, COUNT, MAXIMIZE, etc. can be declared as you would for the scope of a PROG--that is, LOOP creates a BLOCK the same way PROG creates a BLOCK.  LOOP should then be smart enough to use integer or floatp arithmetic to increment counters when it is safe to do so.
(userkeyword ...)
-- Something resembling I.S.OPRS extensibility.  Some versions of LOOP have a notion of user defined paths, which I've found hard to use.