(*
 * The function for is 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 for loop
 *
 *     for i:= <start> to <until> by <by> do
 *         <body>
 *     end;
 *
 * The for function has the following signature:
 *
 *    type ('ins,'inouts,'outs)bodyParms = 'ins*'inouts*'outs;
 *    fun for(start:int, until:int, by:int,
 *       bodyin:bodyParms('i,'io,'o), body:'i*'io*'o->'io*'o): 'o list;
 *
 * The first three parameters are those of a typical imperative for loop --
 * <start>, <until>, and <by> as integers.  The fourth parameter, bodyin, is
 * used as input to the for body, as discussed further below.  The final input
 * is the for-loop <body> as a function of type int*'a->'b.  That is, the
 * <body> is a function that takes two input parameters, of type int and 'a,
 * and returns a value of type 'b, where 'a and 'b can be any types.

 *
 * 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 in some result variable
 *
 * 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
 * result as a function output.  These observations explain the type of the
 * body parameter, int*'a->'b:
 *
 *     (1) the int input is the value of the for variable
 *     (2) the 'a input is for any additional working input values
 *     (3) the 'b output is for the result of the body compuation.
 *
 * In an impertive 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 = 10;
 *     result = 1;
 *     x = for (i=1; i<=10; i++) result *= i+k;

 *

 * 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 impeartive for-loop comunicates its results
 * through some change to a 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 ouput, the imperative for-loop
 * does not accept any values as inputs.  In an imperative language, this fact
 * does not really 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
 * generally 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 fourth input to the for function -- the bodyin
 * parameter.  This input is sent to the for-loop body in addition to the value
 * of the for variable.

 *

 * The preceding discussion has explained all of the input parameters to the
 * for function.  We turn finally to the output, which is declared as type
 * 
 *     'b list
 *

 * where type 'b is the return type of the body function.  Based on what we've
 * discussed thus far, we might expect the return value to be just of type 'b,
 * not a list of 'b.  That is, the for function would return exactly the same
 * value that its body computes, by simply passing it thorough to the caller.

 *

 * There are couple of good reasons to return a list from for, rather than a
 * single value.  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 one
 * considers 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 ('b
 * list) from for, instead of just 'b, 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 extremely simple.  The reader may well note the temporal and spatial
 * inefficiency of for1 -- it computes the head of the reverse of the list
 * returned by for.  A more time/space-efficient version of for1 is left as an
 * exercise for the reader.
 *)

structure ForLoops = struct
    exception EmptyLoop;

    fun for(start:int, until, by, bodyin, body) =
	if start>until then
	    []
	else
	    let
	        val oldbodyin1 = #1(bodyin);
		val oldbodyin2 = #2(bodyin);
		val oldbodyin3 = #3(bodyin);
		val result = body(start, bodyin);
		val newbodyin2 = #1(result);
		val newbodyin3 = #2(result);
		val forresult = newbodyin3;
		val newbodyin = (oldbodyin1, newbodyin2, newbodyin3)
	    in
		forresult :: for(start+by, until, by, newbodyin, body);
	    end;

    fun for1(start, until, by, body, bodyin) =
	hd(rev(for(start, until, by, body, bodyin)))
	    handle Hd => raise EmptyLoop

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 anonyous (lambda) function to define the for-loop
 * body.  Chapter 11, Page 103 of the Ullman's ML discusses anonymous functions
 * in general.
 *)
for(1, 10, 2, nil, fn(i,x)=>
    (print(i); print("\n"))
);

(*
 * The following is the equivalent of the (illegal) C program discussed in the
 * earlier comments.  It may take a little disection to understand fully.
 *)
fun last(l) = let val len = length(l)-1 in nth(l,len) end;
val x = 
    for(1, 10, 1, (10, [1]), fn(i, k_result)=>
	let val result = hd(#2(k_result)) * (i + #1(k_result)) :: #2(k_result)
	in result
	end
    );