stack overflow
– where the beloved site of the same name, gets its name. Let’s investigate how this works.
n!=n * (n1) * ... * 2 * 1
. If the author cares to define the same function recursively you might see
Notice how the definition is made of two parts, a recursive part and a basecase part. This translates nicely to programming. In a functional language we might define the factorial function like the following.
with **************** (0) ******************************** >
******************** (1) **************** n>n*factorial ( n) ******************** **************** (1) ********************)The syntax is slightly different, but the structure is suprisingly similar. This function is however not tail recursive, it is merely recursive.
A slightly rewritten version, whichis
tail recursive, might look like the follwing.
)let(rec) ******************* (factorial acc n) *******************= match
n
with **************** (0) ******************************** >
acc n>factorial(acc (n **********))(
n

1This definition is mostly the same, with the addition of a new parameteracc
, short for accumulator. Introducing an accumulator is often necessary to be able to rewrite a function to be recursive. Annoyingly, this version has to be called with an initial value ofacc=1
, so for instance (5!) *********** (would look likefactorial 1 5) .
The crucial difference between these definitions, however, is that the recursive call tofactorial
in this new definition is in "tail position". In a sense, the last operation that is performed in the body of the function definition, is to call itself. This is what allows the compiler to replace the function call with a loop instruction and reuse the same stack frame  because there is no more computation to be done in this stack frame after the recursive call. When it returns, all we do is return that answer. In which case we might as well return the answer directly.
Maybe this becomes more clear if we take a look at the same two definitions rewritten more verbosely, using intermediary variable names and if statements, instead of the more terse mathematicslike syntax above .
)let(rec) ******************* (factorial n)= ifn=(0) **************** then1 else letrest (=(factorial) **************** ( n) ********************  (1) ********************) n********************* )let(rec) ******************* (factorial acc n) *******************= ifn=(0) **************** thenacc else letsofar=acc (************ n letnext (=**************** n) *******************  1 factorial sofar nextThe important difference is that in the first definition there is more work to be done after the recursive call, namely the multiplication
n * rest, while in the second definition, all the work happens before.
One way I like to think about this, is in a mutable way. I think of the last linefactorial sofar next
as an instruction to go back to the top of the function and bindacc
to this new valuesofar
andntonext
. Whenevern==0
and it is time to break out of this looping recursion, we simply return the latest value ofacc
directly to the original caller of the factorial function.
In fact, this is precisely how Clojure implements tail recursion. Instead of detecting it automatically, Clojure forces you to use a special syntax with the special keywordsloop
andrecur
. Here is a Clojure implementation.
(defn[n] (**************** (loop) **************************** [acc1 n n] (**************** (if(=n****** (0) ********************) acc (**************** (recur) **************************** (*accn)
(
dec (n)
**********************
)Again we see the same structure, only expressed using a different syntax.
TrampoliningIn many popular languages such as Python and JavaScript there is no implementation of the language which implements tail recursion, so even if you write your functions in a tail recursive style, it has no effect.
We can however implement implement it ourselves if we want to do many and deep recursions, without blowing the stack, using a technique known astrampolining. In this technique we write our functions in a tail recursive style, but instead of evaluating our function recursively before returning, we instead return the function we want called and the argument we want to call it with. Then we wrap this in a loop which is responsible for calling it repeatedly, until the function signals that it is done. We signal this by returning a special value, such as undefined, instead of the function, when we reach the base case.
Here is an implementation in JS.
constfactorial=(****************** ()**************************************)=>{ if**************** ()===(0) ****************){ return**************** [undefined,acc]
; } return************************************]**************;}*******************;const**************** (trampoline*********************=
f0=>x0
>
{ let
f (=) **************;
let
x
=x0 (*******************; while****************** (**************=****************************** (undefined) **************){ =****************** (f) ************************************ (x ; } returnx;}*******************;
const
fact
=(trampoline) ****************() Factorial);
factcan now be called with[1, 5]
to evalueate the factorial of5
. One thing to note about this example is the use of twoelement lists to represent tuples. The return value offactorial
is a tuple of the function reference (or undefined) and a tuple of the parameters to the function . The parameters of the function is the accumulator and the current value to compute the factorial of.
Notice also the destructuring off
andx
in the while loop, which also reassigns the variables of the same names. For every iteration of the loop,xis the next set of parameters the function will be called with. When the loop is finished and the recursion done, the value of x is the final value of the computation, the logical return value of the recursive function.
****************
****************************** (