ROOT1, ROOT2 := if DISCRIM > 0.0 then sqrt( DISCRIM ) / DENOM, -sqrt( DISCRIM ) / DENOM else 0.0, 0.0 end ifThe if-statement above has arity of two, meaning it returns two values. This code fragment is a multi-assignment of two values to two names. Each branch of the if-statement within the assignment must return two values. Further, the corresponding result values in each branch must have the same types; this means that if an if-statement's then-clause returns two values, say an integer and an array of reals, in that order, then the else-clause must also return an integer and an array of reals, in that order. In the example above, both values are of type real, as seen by the literal zeroes in the else-branch. In any multiple-valued expression, the number of returned values must equal the number of names they are assigned to, and the names and values associate from left to right. This means that the statement we are considering, above, is equivalent to the following:
ROOT1 := if DISCRIM > 0.0 then sqrt( DISCRIM ) / DENOM else 0.0 end if; ROOT2 := if DISCRIM > 0.0 then -sqrt( DISCRIM ) / DENOM else 0.0 end if;In a multiple-valued expression (also termed a multi-expression), the individual component expressions or values are separated by commas, as shown above. The reader is cautioned not to become confused by the semicolons in the program text; semicolons are statement separators. The rules for constructing and evaluating expressions, even those of multiple arity, are likely to be intuitive to anyone with a background in basic mathematics.
let one := sin(x)*sin(x) + cos(x)*cos(x); two := sqrt( one+one+one+one) three := one + two in one, two, three, sqrt(two) end letThis shows the names "one", "two", and "three" being assigned values in the let-clause, and demonstrates the utility of the let-in statement. The name "one" gets a value based on several function calls; "two" gets a value that uses the value of "one"; and "three" gets a value based on both the previous values. Then in the in-clause, all three names are used to specify four values, which constitute the value of the let-in statement. This let-in statement has arity four.
The if-statement and the let-in statement typify statements that can have multiple arity. The let-in statement brings us to the idea of nested scopes. Each instance of a let-in statement constitutes a scope wherein names may be assigned to values, or defined. Names used in such a way are invisible outside the let-in statement in which they are defined. This allows names to be reused inside nested scopes, and such nesting can go on to effectively any depth. Here is an absurd example of such a legal (but unwise) nesting:
let x := 1; y := let x := 2; y := 3 in x + y end let in x + y end letThe value of this nested pair of let-in statements is 6, and in spite of its tendency to confuse human readers, it is legal Sisal syntax. Many clearer and more compelling uses for statement nests can be found, and will readily occur to an experienced Sisal programmer.
In both the if-statement and the let-in statement, the statements are terminated by an end-if and end-let, respectively. These bracketing keywords help both the compiler and the reader keep track of where they are in the code, and establishes the pattern for all Sisal statement types. Each of these two statement types can be used together and nested in any convenient way, as long as commas, semicolons, and end-keywords are placed correctly.
The best way to appreciate such syntactic elements is to use them, so let us now go on to some exercises.