We’ve defined even in a very natural way: an value is even when it’s zero, or when it’s odd after subtracting one. Odd is defined in a similar way.

Let’s ignore the problem this code has with negative integers, and the fact that an O(n) algorithm for computing even and odd is not ideal, and focus on a single problem: this code breaks for large n:

scala> even(5000)
res3: Boolean = true
scala> even(50000)
java.lang.StackOverflowError
at Trampolines$Stack$.odd(trampolines.scala:14)
at Trampolines$Stack$.even(trampolines.scala:9)
at Trampolines$Stack$.odd(trampolines.scala:14)
at Trampolines$Stack$.even(trampolines.scala:9)
at Trampolines$Stack$.odd(trampolines.scala:14)
at Trampolines$Stack$.even(trampolines.scala:9)
...
at Trampolines$Stack$.even(trampolines.scala:9)
at Trampolines$Stack$.odd(trampolines.scala:14)

In the remainder of this post we look at various strategies for preventing recursive programs from running out of memory.

But what’s the problem?

The problem manifests itself in the stack trace: even calls odd, then odd calls even, which calls odd again. Each of these invocations causes the creation of a stack frame, eating away the space that’s reserved for them.

Keeping all the stack frames would not really be necessary: when even calls odd, that is the very last thing that the even function will ever do! When odd returns, even itself returns immediately with that same value.

When we compute even(500), after a bunch of recursive calls, the 501st stack frame will be created, for the invocation of even(0), which returns true, and the 500 method calls that are left on the stack will one after another all return that same value, until the whole stack is unwinded.

Some languages or runtimes are smart enough to figure out that if a calling function will simply return the value that a function it calls returns, it does not need to allocate a new stack frame for the callee, but can reuse the stack frame of the caller for that.

Unfortunately, the JVM does not support this.

And so Scala, as long as it sticks to JVM method calling semantics, also can not support this.

A bit of light at the end of the tunnel

Luckily, there is one special case of tail calls which Scala treats differently, and that is the case of tail recursion. If a method does a tail call to itself, it’s called tail recursion, and Scala will rewrite the recursion into a loop that does not consume stack space.

Many recursive algorithms can be rewritten in a tail recursive way. For our even and odd problem, this is a possible solution:

We’ve rewritten even to be tail-recursive, and now Scala will optimize it for us:

scala> even(500000000)
res25: Boolean = true

it no longer blows up for large numbers.

The @tailrec annotation is useful in these cases. It does not change how the function will be compiled, but it does trigger an error if the function is not actually tail-recursive. So this will protect you from accidentally changing a function that you want to be tail-recursive function into something that’s no longer tail recursive.

You can try this out by putting the @tailrec annotation on the first and the latest version of even that we’ve defined.

Trampolines

Another way of solving the problem of consuming too much stack space, is by moving the computation from the stack to the heap.

Instead of letting the even function recursively call odd, we could also let it return some recipe to it’s caller, how to continue with the computation. Let’s look at the following code:

Now we have expressed even and odd in their natural, mutually recursive way, and we still have a stack safe interpreter:

scala> run(even(5000000))
res1: Boolean = true

The disadvantage of this is that this is significantly slower. Unfortunately, we can’t seem to have our cake and eat it too :(

This strategy is sometimes called trampolining, because instead of creating a big stack, we go up to even, then down to run, then up to odd, then down to run, then up to even, down to run, etcetera. The size of our stack keeps growing and shrinking by one frame for every step in the computation. This looks a lot like going up and down on a trampoline :)

Generalizing

There is no need to specialize our little language to computing even and odd. We can also make a little language that can express recursion in a general way:

Recursion and Trampolines in Scala

Here our even and odd functions don’t return domain specific values, but a general value that indicates whether the computation is done, or whether more steps are needed. The latter includes the next step as a by-name parameter, that the tail recursive runner function can call.

Note that our run function is no longer tied to computing even and odd, it can compute anything.

TailRec in the standard library

Something similar in spirit, but with a better implementation is also available in the Scala standard library: