Mutual Tail-Recursion
Mutually tail-recursive functions are commonly encountered in practice.
Assume that foo and bar are two mutually defined
functions. In the body of either foo or bar, a tail-call
to foo or bar is referred to as a mutually tail-recursive
call. If every call to foo or bar in the bodies of
foo and bar are tail-call, then foo and
bar are mutually tail-recursive. Mutual recursion involving more
functions can be defined similarly. As an example, the following two
functions isEvn and isOdd are defined mutually
recursively:
The call to
isOdd in the body of
isEvn is
a mutually tail-recursive call, and the call to
isEvn
in the body of
isOdd is also a a mutually tail-recursive call.
Therefore,
isEvn and
isOdd are mutually tail-recursive.
In order to turn mutually recursive tail-calls into jumps, the
bodies of the involved mutually recursive functions need to be
combined. The keyword fn* in ATS is introduced for this
purpose precisely. For instance, let us replace fun with
fn* in the code above:
Then the following C code is essentially generated from compiling
these two mutually defined functions:
Note that mutually recursive functions can be combined in such a manner
only if they all have the same return type. In the above case, both
isEvn and
isOdd have the same return type
bool.
When translating C code involving embedded loops, we often encounter the
need for mutual tail-recursion. For instance, the following C code prints
out ordered pairs of digits:
A straightforward translation of the C code into ATS (in the style of
functional programming) can be done as follows:
Evidently,
loop1 and
loop2 are defined mutually
tail-recursively. The use of the keyword
fn* ensures that these
two functions are translated into the equivalent of two loops in C, which
require only a fixed amount of stack space to run.