[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

LOOP macros.



Chris Riesbeck and I have both ported iteration macros to T.  These macros
provide syntax for writing Knuth-Loops-- that is, the infinite loops with
arbitrarily placed top-level exit statements that Knuth describes in his
paper "Structured Programming with GOTO Statements".  This message is a
proposal for merging Chris's and my LOOPs.  I'm sending it to T-Discussion
for two reasons:

    a) This macro represents a style of control construct that is in almost
       universal use at Yale in all dialects of Lisp.  Programmers in
       TLisp, MacLisp, and Franz Lisp all use it.  I would guess that, in
       the U editor, I use it almost as much as I use IF or COND, and quite
       a bit more than I use MAP.

    b) T does not provide this construct.  The T implementors do not use
       this construct, and strenuously object to its inclusion in standard
       T.

Why should this macro be included in standard T?  Why not?  If not, how
should this sort of facility be provided in T?


Merging the TLisp and Franz Lisp/ELLISP loop
============================================

This is the TLisp loop, as described by Chris:

(LOOP (INITIAL -(var val inc)-)
      (BEFORE -exps-)
      (WHILE exp)
      (DO -exps-)
      (NEXT -(var val)-)
      (UNTIL exp)
      (AFTER -exps-)
      (RESULT exp) )

This is the Franz/ELLISP loop macro:

(LOOP (INITIAL v1 e1 v2 e2 ...)
      (BEGIN -exps-)
      (CATCH label)                 ;;; LABEL is a synonym for CATCH
      (DECLARE ...)                 ;;; Doesn't generate anything for T yet.
      (WHILE expr)
      (UNTIL expr)
      (INCR i FROM|.IN.|.IN|IN.|IN e1 [TO e2] [BY e3])
      (DECR i FROM|.IN.|.IN|IN.|IN e1 [TO e2] [BY e3])
      (FOR x IN l)
      (DO -exps-)
      (NEXT v1 e1 v2 e2 ...)
      (RESULT -exps-) )

As you can see, (if you know what all of the keywords mean), the TLisp loop
provides a subset of the functionality of the Franz/ELLISP loop.  The
following changes would have to be made to the F/E loop to make it
completely subsume the TLisp loop:

    1.  The only important syntactic difference is the extra layer of
    parenthesis surrounding the constituents of the INITIAL and NEXT
    clauses in TLisp.  I happen to like the TLisp syntax, so I'm quite
    willing to change over to it, and convert all of my code to the new
    syntax.  I thought about expanding the syntax to allow both kinds, but
    this was too ambiguous even for me.

    2.  The TLisp BEFORE is called BEGIN in Ellisp.  BEFORE is a better
    name, so I hereby change the name for this construct from BEGIN to
    BEFORE, with BEGIN accepted as a synonym.

    3.  The TLisp AFTER clause makes up for a deficiency in TLisp's RESULT
    clause-- that it takes just one expression, rather than a list of
    expressions.  The F/E Loop doesn't have this problem, so there is no
    reason for the AFTER clause.  I find (LOOP...(AFTER e1 e2) (RESULT e3))
    much harder to understand than (LOOP...(RESULT e1 e2 e3)).  For this
    reason, I would rather not implement AFTER.  You could take care of
    this in the TLISP_TO_T conversion.

So the proposed loop has the syntax:

(LOOP (INITIAL (v1 e1 inc1) (v2 e2 inc2) ...)
      (BEFORE -exps-)               ;;; BEGIN is a synonym for BEFORE.
      (CATCH label)                 ;;; LABEL is a synonym for CATCH
      (DECLARE ...)                 ;;; Doesn't generate anything for T yet.
      (WHILE expr)
      (UNTIL expr)
      (INCR i FROM|.IN.|.IN|IN.|IN e1 [TO e2] [BY e3])
      (DECR i FROM|.IN.|.IN|IN.|IN e1 [TO e2] [BY e3])
      (FOR x IN l)
      (DO -exps-)
      (NEXT (v1 e1) (v2 e2) ...)
      (RESULT -exps-) )

The INCR/DECR syntax has been extended to offer support for T's
half-open intervals:

    (INCR i .IN. 0  TO 10)  varies i in 0..10  FROM is a synonym for .IN.
    (INCR i .IN  0  TO 10)  varies i in 0..9
    (INCR i  IN. 0  TO 10)  varies i in 1..10
    (INCR i  IN  0  TO 10)  varies i in 1..9
    (DECR i .IN. 10 TO  0)  varies i in 10..0  FROM is a synonym for .IN.
    (DECR i .IN  10 TO  0)  varies i in 10..1
    (DECR i  IN. 10 TO  0)  varies i in 9..0
    (DECR i  IN  10 TO  0)  varies i in 9..1

STEP is a synonym for INCR, and DOWNSTEP (ugh) is a synonym for DECR.  I
implemented an ad hoc UNWIND-PROTECT clause in the LOOP I did within Franz,
I'll put it back into the LOOP macro as soon as T's UNWIND-PROTECT doesn't
cons.

If T had Lisp's PROG, then LOOP would be translated into this:

(CATCH -exit-label-                 ; from CATCH.
     (PROG -prog-vars-              ; from INITIAL, INCR, DECR, and FOR.
           -declaration code-       ; from DECLARE, INCR, and DECR.
           -initialization code-    ; from INITIAL, INCR, DECR, and FOR.
           -before-code-            ; from BEFORE.
      LOOP-TOP
           -iteration tests-        ; from INCR, DECR, and FOR.
           -body and tests code-    ; from DO, WHILE, UNTIL, and NEXT.
           -step code-              ; from INCR, DECR, and FOR.
           (GO LOOP-TOP)
      LOOP-EXIT
           -result code-))          ; from RESULT.

But it's actually translated into this TC-compilable T:

    (CATCH -exit-label-
         (LET ((var1 init1))
                -more-variable-bindings-
         (LET ((LOOP-EXIT (LAMBDA () -result code-)))
         (LABELS ((LOOP-TOP
                   (LAMBDA ()
                       (IF -exit condition- (LOOP-EXIT)
                           (IF -exit-condition- (LOOP-EXIT)
                               -more-body-
                               -increment and step code-
                               (LOOP-TOP))))))
              -before-code-
              (LOOP-TOP)))))

LOOP-TOP is a tail recursive form, so this all compiles into JUMPs.
-------