Local variables
AUSTRA is a functional language, so it has a functional technique for declaring what in a procedural language would be temporal or local variables.
The functional technique for declaring local variables in a formula is the let clause.
let m = matrix::lrandom(5),
m1 = m * m',
c = m1.chol in
(c * c' - m1).aMax
In the above example, a lower triangular random matrix is computed, and it is
multiplied by its transpose. Then, the Cholesky transform is calculated and finally
we check that the transform is valid, evaluating the absolute maximum of the
matrix difference.
The m, m1 and c variables only exist while the formula is being evaluated.
As the example shows, each variable defined in the let clause can use any of the previously declared variables in the same clause.
When writing several statements in a script, let/in clauses are valid only for the statement they precede, but not for other statements:
let m = matrix::lrandom(5),
m1 = m * m',
c = m1.chol in
(c * c' - m1).aMax;
-- The next statement cannot use "m".
m
If you need a local variable to be available for all statements that follow in a script, you must use a variant of let which does not terminate with an in keyword, but with a semicolon:
let m = matrix::lrandom(5);
-- Now, "m" is available for the rest of the script.
let m1 = m * m',
c = m1.chol in
(c * c' - m1).aMax;
-- The next statement is valid.
m
Some functional languages, as Haskell, feature another construct for abstracting sub-expressions. Haskell, for instance, offers both let and where. let is located before the expressions that make use of it, and where comes after the main expression.
In AUSTRA, we prefer let, for the sake of Code Completion. So far, I cannot think of any use for where that cannot be solved better with let.
Functions can be defined in let clauses. For instance:
let mcd(a, b: int): int = if a % b = 0 then b else mcd(b, a % b) in
mcd(80, 140)
In the above example, the function is defined in a let/in clause, but it could also be defined as a script-scoped local function.
Since mcd is recursive, its return type must be declared in the function header.
Function definitions may have their own local variables, as in this variant of the above example:
let mcd(a, b: int): int =
let m = a % b in iff(m = 0, b, mcd(b, m)) in
mcd(80, 140)
This way, we save one evaluation of the remainder.
Local functions may also be declared inside other functions. For instance, this code defines a function for the factorial, but uses an intermediate function that can be evaluated using tail recursion, for efficiency:
let fact(n: int) =
let f(n, acc: int): int = iff(n <= 1, acc, f(n - 1, n * acc)) in
f(n, 1);
fact(10);
Please note that the in keyword applies to the right-side of the definition of factorial. The let clause that defines factorial, on the contrary, is a script-level clause, with no associated in.