Recursion
===============
As mentioned above, axis strict scoping rules makes defining recursive
functions a bit more difficult than most programming languages do. It used to
be somewhat of an art to define a recursive function (it could, and can still,
be achieved by calling a function held in a variable to which the recursive
function itself is assigned), but starting with version 0.9 of axis there is
a keyword 'rec_fun' that make this automatic (at least for simple recursive
functions; complicated mutually recursive definitions still require some
effort). Basically take the function definition as one would write it if
recursion were directly allowed, and the insert 'rec_fun' before the recursive
name at its (function) definition. The only restriction is that the return
type must be explicitly specified (while it is option for ordinary functions);
failing to do so after rec_fun will result in a syntax error.
Here is the classical (but silly, as using a loop would do quite as well)
example of the factorial function 'fac'::
atlas> set rec_fun fac (int n) = int: { return type int is specified }
if n<=0 then 1 else n*fac(n-1) fi
Defined fac: (int->int)
atlas> fac(7)
Value: 5040
However, in practice it is often better to achieve recursion locally rather
than directly for the function defined by 'set', as this will allow the latter
to do some preliminary things that are not to be repeated at each recursive
call. For instance if in the above definition one wants to signal negative
arguments as errors rather than returning the (mathematically meaningless)
value 1 for them, this can be done as follows (the example also shows that the
recursive function need not have the same name as the global one::
atlas> set fac (int n) = { no return type needed here }
if n<0 then error("factorial of negative number ",n," is not defined") fi;
let rec_fun f(int n) = int: { return type required here }
if n<=0 then 1 else n*f(n-1) fi
in f(n)
Since the local function f is defined only to be immediately called, one can
even make the recursive function anonymous to the outside, as follows::
atlas> set fac (int n) = { no return type needed here }
if n<0 then error("factorial of negative number ",n," is not defined") fi;
(rec_fun f(int n) = int: if n<=0 then 1 else n*f(n-1) fi) (n) { call it! }
Finally, one could avoid making a new (local) recursive function each time the
global function is called by postponing the introduction of its arguments;
this gives the following (optimal though maybe not optimally readable) style::
atlas> set fac = { no arguments yet }
let rec_fun f(int n) = int: if n<=0 then 1 else n*f(n-1) fi { build once }
in { now comes that actual definition of fac, an anonymous function }
(int n): { introduce argument of fac }
if n<0 then error("factorial of negative number ",n," is not defined")
else f(n) { call the recursive function }
fi