
Tail calls, tailrec and trampolines (2009) - pilingual
http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html
======
xiaq
Tail call optimization is a neat idea, but I have always felt that it is too
magical. You cannot opt out of it. Clojure's approach strikes a good balance:
Clojure doesn't do TCO, but it gives you "recur" as an explicit way to do tail
recursions that don't consume stack space:
[https://clojuredocs.org/clojure.core/recur](https://clojuredocs.org/clojure.core/recur)

Clojure has also had "trampoline" in core since 1.0, which was released in the
same year this article was written:
[https://clojuredocs.org/clojure.core/trampoline](https://clojuredocs.org/clojure.core/trampoline).
I have no idea how well it performs though.

If your language has "goto", it is very easy to manually optimize mutually
recursive functions. One of the few valid use cases of "goto" today. (As
someone who writes a lot of Go, I have always suspected this use case is why
Go still has "goto".)

~~~
sedachv
> One of the few valid use cases of "goto" today.

Manually rewriting recursive algorithms is one of the least valid use case of
_GOTO_. You need _GOTO_ for low-level concurrency, IO multiplexing, and
transpiling:

[https://github.com/vsedach/Eager-
Future2/blob/master/future....](https://github.com/vsedach/Eager-
Future2/blob/master/future.lisp#L79)

[https://github.com/vsedach/Vacietis/blob/master/compiler/imp...](https://github.com/vsedach/Vacietis/blob/master/compiler/implementation.lisp#L179)

------
taylodl
JavaScript doesn't optimize tail recursion. I wrote about this, and how to use
trampolines, a few years back:
[https://taylodl.wordpress.com/2013/06/07/functional-
javascri...](https://taylodl.wordpress.com/2013/06/07/functional-javascript-
tail-call-optimization-and-trampolines/)

~~~
k__
Doesn't JavaScript core optimize it?

~~~
richdougherty
Here's the state of tail-call optimisation in JavaScript:

[https://kangax.github.io/compat-table/es6/#test-
proper_tail_...](https://kangax.github.io/compat-table/es6/#test-
proper_tail_calls_\(tail_call_optimisation\))

Discussion on this (apparently controversial!) feature:

[https://github.com/kangax/compat-
table/issues/819](https://github.com/kangax/compat-table/issues/819)

~~~
k__
Yes, I know about the controverse.

People say it blows up the stack traces, so it should be opt-in.

But it seems that Node.js and Safari have it, and as far as I know Safari uses
JavaScript Core, which is also used for React-Native.

------
Shoothe
Maybe it would be useful to mention that debugging can be more difficult when
using tail calls as previous stack frames are not available.

~~~
srean
But isn't the whole point of tail recursion not to have those stack frames ?
Iterative alternative would not have those stack frames either. I don't hear
that complain for iterations or the complain that the previous version of the
iteration variable has been overwritten. Tail recursions have the same thing,
the 'stack variables' get overwritten.

With time travelling debuggers it might be possible to look at those, but
that's a whole new topic.

~~~
pdpi
> But isn't the whole point of tail recursion not to have those stack frames

Sure. But a recursive function can have errors happen in one call that are
only detected a few levels down. Given a full call stack, you have a trace of
the execution thus far that gives you information about where, exactly, things
went wrong. With TCO, you're just left with the original call at the top, and
the failed state at the bottom, and no snapshot of what happened in the
meanwhile.

~~~
srean
What you say is indeed true, but only partly so. The state of affairs would be
no different in the corresponding iterative solution. I say 'partly' because
you could put a break point at the entry point or before the call. One would
be able to debug the recursion just as one would debug the corresponding loop.

------
jvican
Note that there is a compiler plugin for mutual recursion in Scala.
[https://github.com/wheaties/TwoTails/blob/master/README.md](https://github.com/wheaties/TwoTails/blob/master/README.md)

------
avodonosov
It's a pity scala didn't get continuations in the end.

------
sorokod
>Unfortunately for Scala programmers, the JVM doesn't perform this
optimisation.

Kotlin performs tailrec optimisation and is compiled to jvm.

~~~
iainmerrick
A little further down it says “Luckily, even without JVM support, the Scala
compiler can perform tail-call optimisation in some cases.”

~~~
jonalmeida
I'd say the full quote to be, "Luckily, even without JVM support, the Scala
compiler can perform tail-call optimisation in some cases. The compiler looks
for certain types of tail calls and translates them automatically into loops."

This wouldn't be actual TCO in that case.

~~~
iainmerrick
If it’s done right, there would be no observable difference.

------
eklavya
Maybe 2009 should be added to the title.

~~~
sctb
Updated. Thanks!

