method (automatically) convert functions to tail recursion?
So there are two parts:
- Recognizing that a given recursive function can be transformed into a tail recursive form and do this transformation
- efficient tail calls, so the conversion is worth it.
Convert recursion to tail recursion
Apparently relatively straightforward to syntactically recognize tail recursive definitions. In the end, "tail recursive" means that the call appears syntactically in the tail of the expression.
eg. Schema people describe:
just compile the appropriate self-language, since the transitions are not enough to achieve full tail recursion. Instead, we syntactically divide all subexpression positions in the source language into two classes: tail (or abbreviations) and subtasks. In a simple expression (if predicate consistent alternative), predicate is the position of the subtask, while both subsequent and alternative position reductions. This syntactic concept can easily be extended to arbitrarily nested subexpressions.
Convert functions to tail calls
The hard part of your question is optimization for identifying and transforming recursive computations of candidates into tail recursive ones.
The GHC uses one link, which uses a built-in and wide range of simplification rules to collapse recursive calls until their main recursive tail structure remains.
Tail elimination
Once you have your functions in tail recursive form, you would like it to be implemented efficiently. If you can create a loop, this is a good start. If your target machine doesn’t do this, then tail tail removal will require a few tricks. To quote Odersky and Schinz below,
Over the years, various methods have been proposed to eliminate common (and not only recursive) tail calls, mainly for compilers targeting C.
... put the whole program in a large function and simulate function calls using direct jumps or switch statements inside that function.
... A popular method is to use a trampoline. Trampoline - external which repeatedly causes internal function. Each time an internal function wants the tail to call another function, it does not call it directly, but simply returns its identity (for example, as a closure) to the trampoline, which then performs calling itself. Thus, unlimited stack growth can be avoided, but some indicators are inevitably lost, mainly because all calls made by the trampoline are calls to statically unknown functions.
Another method is Henry Bakers "Cheney on M.T.A." With it, the program must first be converted to Continued Transfer (CPS), so the inclusion of all calls in tail calls, and then can be composed as usual. At run time, when the stack is about to overflow, the current continuation is built and returned (or longjmped) in the “Waiting” trampoline at the bottom of the call stack.