(* * The function for is a functional for-loop. It performs a computation * comparable to the following C for loop: * * for(i = ; i <= ; i += ) { * ; * } * * or the equivalent Modula-2 for loop * * for i:= to by do * * end; * * The for function has the following signature: * * fun for(start:int, until:int, by:int, bodyin:'a, body:int*'a->'b):'b list * * The first three parameters are those of a typical imperative for loop -- * , , and 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 as a function of type int*'a->'b. That is, the * 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 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 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 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 body(start, bodyin) :: for(start+by, until, by, bodyin, body) 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 );