One issue that must be addressed in any language design is the definition of the sequencing of elementary operations. One extreme corresponds to operational definitions in which this sequencing is defined exhaustively, for all features of the language, and for all possible elementary operations.
The view adopted in Ada, following the Algol 68 concept of collateral evaluation, uses a somewhat simpler mental model of sequencing. We consider that time differences only matter at certain specific points of the program - mainly at the semicolons that terminate statements and declarations. This means that the sequencing of certain actions that occur between two consecutive semicolons is not necessarily defined by the language. Consider for example, the following assignment statements:
OUT_ROW(FLOOR(X)) := IN_ROW(CEIL(Y)); X := SIN(Y)*COS(Y) - TAN(Z); |
where we assume OUT_ROW and IN_ROW to be arrays, FLOOR, CEIL to be functions, and X, Y, Z to be variables. For assignments statements, the Ada reference manual specifies that the evaluation of the left and right hand sides is done in some order that is not defined by the language. This means that the language does not define which of FLOOR and CEIL is called first. Similarly, whereas the precedence rules require the right hand side of the second assignment to mean
(SIN(Y)*COS(Y)) - TAN(Z)
the rules of expression evaluation leave the order of evaluation of the function calls undefined. Thus it would be possible for the functions to be called in any of the following orders:
(1) TAN (2) SIN (3) COS (1) TAN (2) COS (3) SIN (1) SIN (2) COS (3) TAN (1) COS (2) SIN (3) TAN |
The only (and partial) order imposed comes from the ordering of statements and from the logic of operations. Thus the ordering of the two statements requires that a function call (such as FLOOR) of the first assignment statement occur before a function call (such as SIN) of the second. Similarly, the logic of operations requires that the multiplication be performed after the evaluation of its two operands (SIN(Y) and COS(Y)).
The Ada reference manual further specifies (RM 1.6) that if different parts of a given construct are to be executed in some order that is not defined by the language, then the construct is incorrect if execution of these parts in a different order would have a different effect: this kind of error is called an incorrect order dependence.
In terms of programming methodology, this means that we must consider the flow of time to be given by the sequencing of statements, each simple statement being considered as an indivisible action. In the above example, the first assignment would be incorrect if its effect depended on the order of evaluation of FLOOR and CEIL. Should this order actually matter, then the proper way to write the program would be to give a sequence of assignments that defines the intended order explicitly. For example:
U := FLOOR(X); -- guarantees that FLOOR V := CEIL(Y); -- is called before CEIL OUT_ROW(U) := IN_ROW(V); |
Similar considerations apply to expression evaluation. All constituents of an expression (aside from short-circuit control forms) must be evaluated, although the evaluation order is not defined for all terms. Hence if an exception is raised by the evaluation of some term, then this exception cannot be avoided. In this sense an expression such as
A = 0 or X/A > 10
although syntactically correct, is not proper since the validity of the right operand of or depends on the value of the left operand. Whenever there is such a dependence, it should be made explicit by means of conditional statements, or by short-circuit control forms (see 3.10), in order to emphasize the possibility of incomplete evaluation. For example:
A = 0 or else X/A > 10
Note finally that whenever order is not defined, the reference manual uses the phrase in some order that is not defined, rather than the phrase in any order. The intent of the chosen wording is to leave the order undefined but nevertheless require that it be done in some order, and thus exclude parallel evaluation.
To illustrate this point, consider again the calls of FLOOR and CEIL, and assume that the values obtained do not depend on whether FLOOR or CEIL is called first. It is nevertheless possible that parallel evaluation of FLOOR and CEIL will yield a different effect. Thus FLOOR and CEIL could be memo-functions, which remember past intermediate results (for efficiency). Should some of these intermediate results be used by both FLOOR and CEIL, then it would be possible for interleaved executions of these functions to deliver different - and probably incoherent - results.