Last call modulo constructor

One common situation in declarative languages is where there is almost a tail call, in the sense that there are no computations or calls (or ABI infrastructure, such as exception-handling code) between the call and the return.

An especially common occurrence is where the only thing remaining is a constructor. Consider, for example, list append in ML:

fun append [] l2     = l2
  | append (h::t) l2 = h :: append t l2;

The only thing that happens after the recursive call to append is the construction of the cons cell. So this could be implemented as a tail call or loop where the constructor occurs before the call, and a reference or pointer is passed to the tail call.

You may need to implement this as a worker/wrapper, with the wrapper satisfying the language's ABI. In a hypothetical C-like backend:

ml_object*
ml_append(ml_object* l1, ml_object* l2)
{
    ml_object* return_value = nullptr;
    ml_append_worker(l1, l2, &return_value);
    return return_value;
}

void
ml_append_worker(ml_object* l1, ml_object* l2, ml_object** return_value)
{
    if (is_nil(l1)) {
        *return_value = l2;
    }
    else {
        *return_value = make_cons();
        return_value->car = l1->car;
        return_value->cdr = nullptr; /* Garbage collection needs to know about this! */
        /* The following is now a tail call. */
        ml_append_worker(l1->cdr, l2, &return_value->cdr);
    }
}

As with all worker/wrapper arrangements, the wrapper is an excellent candidate for inlining.

Middle recursion

A second option is what Mercury calls middle recursion. Using Mercury's terminology, this pattern:

a_function()
{
    if (is_base_case()) {
        base_case();
    }
    else {
        down();
        a_function();
        up();
    }
}

...can be transformed into this, using an explicit stack.

a_function()
{
    while (!is_base_case()) {
        down();
        push_local_variables_onto_stack();
    }
    base_case();
    while (stack_has_elements) {
        pop_local_variables_off_stack();
        up();
    }
}

The stack can sometimes be replaced by a counter if the data stored across the recursive call is trivial or nonexistent.

A common hand-implementation of quick sort essentially does this, using an explicit stack for one recursive call and a loop for the other.

Answer from Pseudonym on Stack Exchange
🌐
GeeksforGeeks
geeksforgeeks.org › dsa › tail-vs-non-tail-recursion
Tail vs. Non-Tail Recursion - GeeksforGeeks
October 6, 2023 - Tail recursion can often be optimized ... reused for the recursive call. Non-tail recursion typically uses more stack space because each recursive call creates a new stack frame....
🌐
Baeldung
baeldung.com › home › core concepts › programming › tail vs. non-tail recursion
Tail vs. Non-Tail Recursion | Baeldung on Computer Science
March 18, 2024 - In this article, we explained the difference between the tail and non-tail recursion. The functions of the former type can reuse the existing stack frame, so they save memory and avoid the stack overflow error. Also, they finish the calculation after processing the base case, so they don’t traverse the call stack back to the original frame.
Discussions

implementation - How to optimize non-tail recursion? - Programming Language Design and Implementation Stack Exchange
Tail recursion, where a function calls itself as the last step, is straightforward to optimize as to prevent unbounded stack growth: tail call optimization applies. However, this doesn't apply to More on langdev.stackexchange.com
🌐 langdev.stackexchange.com
July 13, 2023
algorithm - What is tail recursion? - Stack Overflow
I did write a long post titled "Understanding Tail Recursion – Visual Studio C++ – Assembly View" ... N(x-1) is the last statement in the function where the compiler is clever to figure out that it can be optimised to a for-loop (factorial) 2016-12-31T10:28:12.733Z+00:00 ... My concern is that your function N is exactly the function recsum from the accepted answer of this topic (except that it is a sum and not a product), and that recsum is said to be non ... More on stackoverflow.com
🌐 stackoverflow.com
java - Tail recursion vs non tail recursion - Stack Overflow
I have been through the tail recursion with below definition A recursive function is said to be tail-recursive if the recursive call is the last thing done by the function, there is no need to keep More on stackoverflow.com
🌐 stackoverflow.com
Can anyone give basic examples of tail recursion and non tail recursion for someone who is having trouble understanding the concept?
Haskell is sort of a bad language to learn this in. Your professor may be obsessing over something that mostly isn't an issue as well. That could be the reason for the problem. In a purely stack based language tail-recursion is the ability of the compiler to replace a recursive function with an interactive function. In Haskell any foldl / foldl' call would automatically satisfy this. The problem is Haskell's compiler doesn't guarantee you that you are using a call stack. Lazy evaluation really screws this concept from eager evaluation up. So what you actually want is a concept from Prolog called "Tail recursion modulo cons" or in Haskell lingo "guarded recursion". The Wikipedia article on this topic is quite good: https://en.wikipedia.org/wiki/Tail_call Something like factorial 1 = 1 factorial k = k * factorial (k-1) will fill the call stack because the system must keep all the intermediate results. Hopefully someone more in tune with what your professor wants will give you a better answer. More on reddit.com
🌐 r/haskellquestions
3
5
December 14, 2021
🌐
Reddit
reddit.com › r/programminglanguages › algorithms to turn non-tail-recursive functions into tail-recursive ones
r/ProgrammingLanguages on Reddit: Algorithms to turn non-tail-recursive functions into tail-recursive ones
June 7, 2024 -

Hello everybody. I hope you are doing well.

Compilers can optimize a tail-recursive function to get rid of the overhead of creating additional stack frames. But can they transform a non-tail-recursive function (for example, the classic recursive factorial function), into a tail-recursive function to eventually turn it into imperative code? Are there any existing algorithms to do this?

The problem is to create a generalized algorithm that can work with any recursive function that accepts -or returns- any type of value. I believe it is relatively easy to create algorithms that only deal with integers, for example (however implementing those optimizations would probably introduce a lot of bugs and edge cases).

What distinguishes a tail-recursive function from a non-tail-recursive one? I think the answer to this question is the following:

In the body of a non-tail-recursive function, the function's return value is used as an argument to another function call in the function's body. This creates a need to wait for the function call to return, which requires the creation of additional stack frames.

fac(n) = 
  if n = 1 { 1 }
  else { n * fac (n-1) }

This is essentially the same as this:

fac(n) = 
  if n = 1 { 1 }
  else { MUL (n, fac (n-1)) }

We need to turn this into a function in which it calls itself as a "stand-alone" function call (so, the function call is not an argument to another call). As an alternative, we would need to come up with an algorithm that somehow stores every n in the current stack frame, so we don't have to create a new stack frame every time fac gets called.

I hope this makes sense. I am waiting for your answers.

Guido van Rossum: Tail Recursion Elimination (2009) Dec 29, 2015
r/programming
10y ago
Recursion without stack overflow May 16, 2020
r/ProgrammingLanguages
6y ago
Some misconceptions on tail recursion Apr 26, 2009
r/programming
17y ago
More results from reddit.com
🌐
HowToDoInJava
howtodoinjava.com › home › algorithms › tail recursion vs non-tail recursion
Tail Recursion Vs Non-Tail Recursion
October 10, 2022 - In tail recursion, no other operation is performed after the recursive function call, while in non-tail recursion, an operation is performed on the calculated value.
🌐
Medium
medium.com › @conboys111 › non-tail-vs-tail-recursion-in-javascript-why-memory-usage-matters-c4e648581e39
Non-Tail vs Tail Recursion in JavaScript: Why Memory Usage Matters | by myHotTake | Medium
January 7, 2025 - Every time a function calls itself, it waits for the next function call to finish before it can finish itself. So, it creates a “stack” of operations in memory, which grows with each recursive call.
Top answer
1 of 2
6

Last call modulo constructor

One common situation in declarative languages is where there is almost a tail call, in the sense that there are no computations or calls (or ABI infrastructure, such as exception-handling code) between the call and the return.

An especially common occurrence is where the only thing remaining is a constructor. Consider, for example, list append in ML:

fun append [] l2     = l2
  | append (h::t) l2 = h :: append t l2;

The only thing that happens after the recursive call to append is the construction of the cons cell. So this could be implemented as a tail call or loop where the constructor occurs before the call, and a reference or pointer is passed to the tail call.

You may need to implement this as a worker/wrapper, with the wrapper satisfying the language's ABI. In a hypothetical C-like backend:

ml_object*
ml_append(ml_object* l1, ml_object* l2)
{
    ml_object* return_value = nullptr;
    ml_append_worker(l1, l2, &return_value);
    return return_value;
}

void
ml_append_worker(ml_object* l1, ml_object* l2, ml_object** return_value)
{
    if (is_nil(l1)) {
        *return_value = l2;
    }
    else {
        *return_value = make_cons();
        return_value->car = l1->car;
        return_value->cdr = nullptr; /* Garbage collection needs to know about this! */
        /* The following is now a tail call. */
        ml_append_worker(l1->cdr, l2, &return_value->cdr);
    }
}

As with all worker/wrapper arrangements, the wrapper is an excellent candidate for inlining.

Middle recursion

A second option is what Mercury calls middle recursion. Using Mercury's terminology, this pattern:

a_function()
{
    if (is_base_case()) {
        base_case();
    }
    else {
        down();
        a_function();
        up();
    }
}

...can be transformed into this, using an explicit stack.

a_function()
{
    while (!is_base_case()) {
        down();
        push_local_variables_onto_stack();
    }
    base_case();
    while (stack_has_elements) {
        pop_local_variables_off_stack();
        up();
    }
}

The stack can sometimes be replaced by a counter if the data stored across the recursive call is trivial or nonexistent.

A common hand-implementation of quick sort essentially does this, using an explicit stack for one recursive call and a loop for the other.

2 of 2
2

If we can assume there is only 1 recursive call then you add a stack of stackframe structs. Then whenever a recursive call happens you push a copy of all local variables to the stack and then replace the parameters and goto the beginning of the function. On a return you pop the copy of the stackframe back into local variables and goto just after the original call site.

When there are 2 call sites you add an indicator of which callsite the recursion happened to the stackframe data and then goto the correct return point.

For mutual recursion you will need to inline one function into the other one.

🌐
GeeksforGeeks
geeksforgeeks.org › dsa › tail-recursion
Tail Recursion - GeeksforGeeks
# The function is not tail # recursive because the value # returned by fact(n-1) is used # in fact(n) and call to fact(n-1) # is not the last thing done by # fact(n) def fact(n): if (n == 0): return 1 return n * fact(n-1) # Driver program to test # above function if __name__ == '__main__': print(fact(5)) ... using System; class GFG { // A NON-tail-recursive function.
Published: January 20, 2026
Find elsewhere
🌐
Wikipedia
en.wikipedia.org › wiki › Tail_call
Tail call - Wikipedia
4 weeks ago - The tail call doesn't have to appear ... return immediately after the tail call, returning the tail call's result if any, since the calling function is bypassed when the optimization is performed. For non-recursive function calls, this is usually an optimization that saves only ...
🌐
@ankurm
ankurm.com › home › tail vs non-tail recursion in java: definitions, examples, and when it matters
Tail vs Non-Tail Recursion in Java: Definitions, Examples, and When It Matters
October 4, 2025 - This is tail-recursive because the last operation is the recursive call whose value is immediately returned. In non-tail recursion, the function must perform work after the recursive call returns (e.g., multiply, add, merge), so the runtime must retain stack frames to complete pending operations.
Top answer
1 of 16
2128

Consider a simple function that adds the first N natural numbers. (e.g. sum(5) = 0 + 1 + 2 + 3 + 4 + 5 = 15).

Here is a simple JavaScript implementation that uses recursion:

function recsum(x) {
    if (x === 0) {
        return 0;
    } else {
        return x + recsum(x - 1);
    }
}

If you called recsum(5), this is what the JavaScript interpreter would evaluate:

recsum(5)
5 + recsum(4)
5 + (4 + recsum(3))
5 + (4 + (3 + recsum(2)))
5 + (4 + (3 + (2 + recsum(1))))
5 + (4 + (3 + (2 + (1 + recsum(0)))))
5 + (4 + (3 + (2 + (1 + 0))))
5 + (4 + (3 + (2 + 1)))
5 + (4 + (3 + 3))
5 + (4 + 6)
5 + 10
15

Note how every recursive call has to complete before the JavaScript interpreter begins to actually do the work of calculating the sum.

Here's a tail-recursive version of the same function:

function tailrecsum(x, running_total = 0) {
    if (x === 0) {
        return running_total;
    } else {
        return tailrecsum(x - 1, running_total + x);
    }
}

Here's the sequence of events that would occur if you called tailrecsum(5), (which would effectively be tailrecsum(5, 0), because of the default second argument).

tailrecsum(5, 0)
tailrecsum(4, 5)
tailrecsum(3, 9)
tailrecsum(2, 12)
tailrecsum(1, 14)
tailrecsum(0, 15)
15

In the tail-recursive case, with each evaluation of the recursive call, the running_total is updated.

Note: The original answer used examples from Python. These have been changed to JavaScript, since Python interpreters don't support tail call optimization. However, while tail call optimization is part of the ECMAScript 2015 spec, most JavaScript interpreters don't support it).

2 of 16
844

In traditional recursion, the typical model is that you perform your recursive calls first, and then you take the return value of the recursive call and calculate the result. In this manner, you don't get the result of your calculation until you have returned from every recursive call.

In tail recursion, you perform your calculations first, and then you execute the recursive call, passing the results of your current step to the next recursive step. This results in the last statement being in the form of (return (recursive-function params)). Basically, the return value of any given recursive step is the same as the return value of the next recursive call.

The consequence of this is that once you are ready to perform your next recursive step, you don't need the current stack frame any more. This allows for some optimization. In fact, with an appropriately written compiler, you should never have a stack overflow snicker with a tail recursive call. Simply reuse the current stack frame for the next recursive step. I'm pretty sure Lisp does this.

🌐
OpenGenus
iq.opengenus.org › non-tail-recursion
Non-Tail Recursion
November 12, 2022 - Tail recursion has the recursive statement in the last part of the program and no other operations are executed later. In “non-tail recursion”, there are still operations to be performed after the recursive call since the recursive statement is the first statement.
🌐
Medium
prasadbylapudi.medium.com › tail-recursion-and-non-tail-recursive-function-which-is-best-971bfbbec96d
Tail Recursion and non-tail recursive function which is best? | by Prasad Bylapudi | Medium
March 14, 2020 - Tail recursion is the last thing that executed by the function called tail-recursive function. In tail-recursion, you do not need to store the state of a child’s value whereas in case of non-tail recursion the child’s value is passed to the parent then the values can be calculated.
🌐
Scribd
scribd.com › presentation › 368540103 › Lecture-13
Understanding Tail and Non-Tail Recursion | PDF | Computer Engineering | Areas Of Computer Science
The document discusses tail and ... like being easily converted to iteration, while non-tail recursion has operations after the recursive call....
🌐
Stack Overflow
stackoverflow.com › questions › 58204963 › tail-recursion-vs-non-tail-recursion
java - Tail recursion vs non tail recursion - Stack Overflow
In both versions, there are multiple recursive calls, and neither are the last thing the function does, so no, it isn't. For the function to be tail recursive, there can only be one recursive call total, and it has to be the last thing in the ...
🌐
Auspham
swe.auspham.dev › home
Tail Vs Non-Tail Recursion | Austin's SWE Notes 🧠
For languages such as C, C++ tails recursion can be optimised by compiler · A function is called non-tail recursion if there are instruction after the recursion call.
🌐
Interview Kickstart
interviewkickstart.com › home › blogs › learn › tail recursion
Tail Recursion | Interview Kickstart
December 18, 2025 - In tail recursion, there is no ... of the recursive call. In non-tail recursion, some operations need to be performed using the returned value of the recursive call....
🌐
Reddit
reddit.com › r/haskellquestions › can anyone give basic examples of tail recursion and non tail recursion for someone who is having trouble understanding the concept?
r/haskellquestions on Reddit: Can anyone give basic examples of tail recursion and non tail recursion for someone who is having trouble understanding the concept?
December 14, 2021 -

I'm studying for my final and tail recursion is something my professor always asked about and sometimes I would be able to answer correctly and other times not. Would anyone be able to give an example of tail recursive function and a non tail recursive function and explain WHY they are?

Top answer
1 of 3
4
Haskell is sort of a bad language to learn this in. Your professor may be obsessing over something that mostly isn't an issue as well. That could be the reason for the problem. In a purely stack based language tail-recursion is the ability of the compiler to replace a recursive function with an interactive function. In Haskell any foldl / foldl' call would automatically satisfy this. The problem is Haskell's compiler doesn't guarantee you that you are using a call stack. Lazy evaluation really screws this concept from eager evaluation up. So what you actually want is a concept from Prolog called "Tail recursion modulo cons" or in Haskell lingo "guarded recursion". The Wikipedia article on this topic is quite good: https://en.wikipedia.org/wiki/Tail_call Something like factorial 1 = 1 factorial k = k * factorial (k-1) will fill the call stack because the system must keep all the intermediate results. Hopefully someone more in tune with what your professor wants will give you a better answer.
2 of 3
4
Recursion, not tail: length [] = 0 length (_:xs) = 1 + length xs Tail recursion: length = l 0 where l a [] = a l a (_:xs) = l (1 + a) xs EDIT: Why? Look at the location of the recursive call; the "tail" position (in Haskell) has the function name at the "top" of the rhs expression (l _ _), not nested inside a call to some other function/operator (_ + length _). As others has said, tail recursion isn't as important in Haskell as it is in other languages. Productivity / guarded recursion makes for better behavior. Though, tail recursion can make sense, if you get the right strictness.
🌐
Placement Preparation
placementpreparation.io › home › blog › dsa programming › tail vs non – tail recursion
Tail vs Non-Tail Recursion in DSA: Key Differences
May 7, 2026 - The result of the recursive call is returned directly. Non-tail recursion: A function is non-tail recursive when some operations still need to be performed after the recursive call returns.
🌐
Lean
lean-lang.org › functional_programming_in_lean › Programming___-Proving___-and-Performance › Tail-Recursion
8.1. Tail Recursion
In the evaluation steps, parentheses indicate recursive calls to NonTail.sum. In other words, to add the three numbers, the program must first check that the list is non-empty. To add the head of the list (1) to the sum of the tail of the list, it is first necessary to compute the sum of the tail of the list: 1 + (NonTail.sum [2, 3])
🌐
Iusb
cs.iusb.edu › ~danav › teach › c311 › c311_5_tailrec.html
C311 Tail Recursion
Recursive Fibonacci Usual recursive implementation: non tail recursive because the results of two recursive calls must be added to obtain the result.