Procedure Expressions

So far we have only discussed function expressions, these being a mechanism for computing new values from the values of identifiers in the current context. Together with assignment this provides us with a means of changing the current context -- to compute a new value for an identifier in the current context, we call a function and then re-assign the identifier with the result of this function. That is we do

> X := f(Y);
where Y is a list of arguments possibly including the current value of X.

At times however using re-assignment to change the value associated with an identifier can be both un-natural and inefficient. Take the problem of computing some reduced form of a matrix. We could write a function that looked something like this,

reduce :=
   function( m )
      local lm;
      ...
      lm := m;
      while not reduced do
         ...
         lm := some_reduction(m);
         ...
      end while;
   ...
   end function;
Note that the local lm is necessary since we cannot assign to the function's formal argument m since it stands for a value (and values cannot be assigned to). Note also that the function is inefficient in its space usage since at any given point in the program there are at least two different copies of the matrix (if the function was recursive then there would be more than two copies!).

Finally the function is also un-natural. It is perhaps more natural to think of writing a program that takes a given matrix and changes that matrix into its reduced form (i.e., the original matrix is lost). To accommodate for this style of programming, Magma includes a mechanism, the procedure expression with its reference arguments, for changing an association of an identifier and a value in place.

Before examining procedure expressions further, it is useful to look at a simple example of a procedure expression. Say we type:

> a := 5; b := 6;
giving the context [ (a,5), (b,6) ]. Say we now type the following:
> p := procedure( x, ~y ) y := x; end procedure;
This gives us a context that looks like [ (a,5), (b,6), (p, PROC(x,~y : y := x;)) ], using a notation analogous to the FUNC notation.

Say we now type the following statement,

> p(a, ~b);
This is known as a call of the procedure p (strictly it should be known as a call to the procedure value associated with the identifier p, since like functions, procedures in Magma are first class values!). Its effect is to change the current context to [ (a,5), (b,5), (p, PROC(a,~b : b := a;)) ]. a and x are called actual and formal value arguments respectively since they are not prefixed by a ~, while b and y are called actual and formal reference arguments respectively because they are prefixed by a ~.

This example illustrates the defining attribute of procedures, namely that rather than returning a value, a procedure changes the context in which it is called. In this case the value of b was changed by the call to p. Observe however that only b was changed by the call to p as only b in the call, and its corresponding formal argument y in the definition, are reference arguments (i.e., prefixed with a ~). A procedure may therefore only change that part of the context associated with its reference arguments! All other parts of the context are left unchanged. In this case a and p were left unchanged!

Note that apart from reference arguments (and the corresponding fact that that procedures do not return values), procedures are exactly like functions. In particular:

a)
procedures are first class values that can be assigned to identifiers, passed as arguments, returned from functions, etc.
b)
procedure expressions are evaluated in the same way that function expressions are.
c)
procedure value arguments (both formal and actual) behave exactly like function arguments (both formal and actual). Thus procedure value arguments obey the standard substitution semantics.
d)
procedures employ the same notion of scope as functions.
e)
procedure calling behaves like function application.
f)
procedures may be declared `forward' to allow for (mutual) recursion.
g)
a procedure may be assigned to an identifier in the initial context.

The remainder of this section will thus restrict itself to looking at reference arguments, the point of difference between procedures and functions.

V2.28, 13 July 2023