(*
 * This file contains the definition of a ForLoops module and some testing of
 * its functions.  The primary function defined in ForLoops is named for, which
 * implements a functional for-loop.  It performs a computation comparable to
 * the following C for-loop:
 *
 *     for(i = <start>; i <= <until>; i += <by>) {
 *         <body>;
 *     }
 *
 * or the equivalent Modula-2 (Ada, Pascal) for loop
 *
 *     for i:= <start> to <until> by <by> do
 *         <body>
 *     end;
 *
 * The for function has the following signature:
 *
 *    fun for(start:int, until:int, by:int, vars:'x, body:int*'x->'x):'x list;
 *
 * The first three parameters are those of a typical imperative for loop --
 * <start>, <until>, and <by> as integers.  The fourth parameter, vars, holds
 * the loop working vars, as discussed further below.  The final input is the
 * for-loop <body>, defined as a function whose signature we'll discuss
 * shortly.  The output of the for function is a list of for-loop results.
 *
 * To understand the workings of a for-loop in a functional language, we need
 * to consider carefully what the <body> of a for loop does in an imperative
 * language.  To whit:
 *
 *     (1) it uses the value of the for variable to perform some computation
 *     (2) the computation typically involves additional working variables
 *     (3) the computation produces a value, stored in one or more of the
 *         working variables, and these variables become the result of the loop
 *         computation.
 *
 * In the functional version of a for-loop, the <body> is a function.  As such,
 * it must take all of its external working values as inputs, including the
 * value of the for variable.  Further, the body function cannot produce its
 * result via side effects on some result variable, but rather must produce its
 * results as functional output.  The output results include any changes to the
 * working variables made within the body of the loop.
 *
 * These observations help to explain the type of the body parameter, which is
 * the function signature int*'x->'x.  In terms of the three points above, the
 * elements of this signature are used as follows:
 *
 *     (1) the integer input contains the current value of the for variable
 *     (2) the for-loop working variables are of generic type 'x
 *     (3) the output of the function body is also of type 'x, so that any
 *         working variable changes are output by the body, and at the end of
 *         the loop, the result variables have been computed.
 *
 * In an imperative language, the for-loop statement does not itself produce a
 * value.  That is, the <body> of the loop computes a value, but the loop
 * itself does not.  E.g., the following C program is illegal, because the C
 * for-loop does not return a value:
 *
 *     int i,k,result;
 *     k = 2;
 *     result = 1;
 *     x = for (i=1; i<=10; i++) result *= i+k;
 *
 * In this style of accumulating for-loop, it might well make sense to have the
 * for-loop return a value, but this is not the case in C.
 *
 * In the functional for-loop, we could mimic the imperative behavior by having
 * the for function return nil or unit.  However, this does not make good
 * sense, because there would be no way for the for-loop body to communicate
 * its results.  I.e., while the imperative for-loop communicates its results
 * through some change to a (persistent) variable, the functional for-loop
 * cannot effect such variable changes, so it must return its results as a
 * return value.  It should be noted that the case where an imperative loop
 * changes more than one variable is handled in the functional for-loop by
 * having the body return some structured value, such as a tuple or list.
 *
 * In addition to not producing a value as an output, the imperative for-loop
 * does not accept any values as inputs.  In an imperative language, this fact
 * does not usually occur to programmers.  I.e., what would the "inputs" to a
 * for loop be?  Well, if we closely examine the body of a C for-loop, we
 * observe that it is in fact a scope, and within this scope there are
 * references to global variables.  For example, in the C program just above,
 * the variables i, k, and result are global to the scope of the for-loop body.
 * But such global variable references are not useful in a functional for-loop.
 * Hence, the for-loop *function* must take as inputs any values that the
 * for-loop *body* needs in its computation.  This observation explains the
 * need for the the vars input parameter.  This input is sent in to the loop
 * body function, along with the value of the for variable.
 *
 * The preceding discussion explained the input parameters to the for function.
 * We now consider for's output, which is declared as type
 * 
 *     'x list
 *
 * where type 'x is the return type of the body function.  Based on what we
 * have discussed thus far, we might expect the return value of the for
 * function to be just of type 'x, not a list of 'x.  That is, the for function
 * could return the final results computed by its body function, not a complete
 * history.
 *
 * There are couple of good reasons to return a list from for, rather than a
 * single result.  First, returning a list makes for a more general function,
 * in that a complete history of computation is returned.  If the user of for
 * wants the last value of the history, she simply extracts the last value of
 * the list.  This is in fact done in the companion for1 function defined
 * below.
 *
 * The second reason for returning a list from for becomes apparent when we
 * consider the return value from a for-loop that executes zero times.  This
 * should be some form of empty or null value.  But, as has been noted
 * elsewhere, there is no universal "null" data value in ML.  The closest we
 * come to it in ML is the nil value for list types.  Hence, if we return ('x
 * list) from for, instead of just 'x, we can use nil to represent the value of
 * a for loop that performs no computation.
 *
 * The proper alternative to the use of nil is to raise an exception.  This is
 * in fact what for1 does, as can be seen in its definition below.
 *
 * We're now ready for the definition of the for and for1 functions, which are
 * really quite simple.  The reader will probably notice the spatial
 * inefficiency of for1 -- it computes the head of the list returned by for.  A
 * more space-efficient version of for1 is left as an exercise for the reader.
 * In addition to for and for1, there is a foreach function, that iterates over
 * the elements of a list, calling a body function on each iteration.
 *
 *)

structure ForLoops = struct
    exception EmptyLoop;

    (*
     * General purpose for-loop, returning a list of all loop iterations.
     *)
    fun for(start: int, until, by, vars:'v, body:int*'v->'v): 'v list =
        if start>until then
            []
        else
            let
                val result = body(start, vars)
            in
                for(start+by, until, by, result, body) @ [result]
            end;

    (*
     * A la for, but returns only the result of the last iteration.
     *)
    fun for1(start, until, by, vars, body) =
        hd(for(start, until, by, vars, body))
            handle Hd => raise EmptyLoop;

    (*
     * C-Shell-style list iterator.
     *)
    local
        fun foreach1(l:'x list, vars:'y, body:'x*'y->'y):'y list =
            if null(l) then
                []
            else
                let
                    val result = body(hd(l), vars)
                in
                    foreach1(tl(l), result, body) @ [result]
                end
    in
        fun foreach(l, vars, body) =
            hd(foreach1(l, vars, body))
                handle Hd => raise EmptyLoop
    end;



end;

open ForLoops;

(*
 * The ML expression that follows is equivalent to the following C for-loop:
 *
 *     for (i=1; i<=10; i+=2) {
 *        printf("%d\n", i);
 *     }
 * 
 * or the following Modula-2 (Adaish, Pascalish) for-loop
 *
 *     for i:=1 to 10 by 2 do
 *        WriteInt(i,3); WriteLn;
 *     end;
 *
 * Note well the use of an anonymous (lambda) function to define the for-loop
 * body.  Chapter 11, pp. 103-4 of Ullman's ML discusses anonymous functions in
 * general.
 *)
for(1, 10, 2, nil, fn(i,x)=>
    (print(Int.toString(i)); print("\n"); nil)
);

(*
 * The ML expression following this comment is the equivalent of the (illegal)
 * C program discussed in the earlier comments.  Here's that example, this time
 * in legal C:
 *
 *    int i,k,result;
 *    k = 2;
 *    result = 1;
 *    for (i=1; i<=10; i++) {
 *        result *= i+k;
 *    }
 *
 * The similarity in style and size of the ML and C versions of this loop is
 * noteworthy, though it may take the C programmer a while to "appreciate" the
 * ML version.
 *
 *)
for1(1, 10, 1, 1, fn(i, result)=>
    let
        val k = 2;
        val result = result * (i + k)
    in
        result
    end
);

(*
 * The preceding example would more likely be written as follows in C:
 *
 *    int i,k,result;
 *    for (i=1, k = 2, result = 1; i<=10; i++) {
 *        result *= i+k;
 *    }
 *
 * which has the following comparable form in ML:
 *)
for1(1, 10, 1, (2, 1), fn(i, (k, result)) =>
    let val result = result * (i + k)
    in (k, result) end
);


(*
 * Additional example uses of for loops follow.
 *)
for(1, 10, 1, 1, fn(i, result) =>
    let val k=2; val result = result * (i + k)
    in result end
);

(*
 * Sum the elements of a list.
 *)
foreach ([1,2,3], 0, fn(x,result)=>
    let val result = result+x in result end);

(*
 * Compute some stats on the element of a list (sum, product, count, average)..
 *)
val l = [1,2,3,4,5,6,7,8];
val (sum, product, count, avg) =
    foreach (l, (0,1,0,0.0), fn(item, (sum, product, count, avg:real)) =>
        let
            val sum = sum + item;
            val product = product * item;
            val count = count + 1;
            val avg = real(sum)/real(count);
        in
            (sum, product, count, avg)
        end);