
Tail Calls, Optimization, and ES6 - luu
http://duartes.org/gustavo/blog/post/tail-calls-optimization-es6/
======
dgreensp
It's important to explain why (or when) TCO is important. You could read a
dozen articles like this that explain what a tail call is in simple terms and
still not know.

The core demographic that cares about TCO is implementers of languages like
Scheme, because if you have TCO:

* Loops can be written as recursive functions. In Scheme, you don't write loops, you write functions that re-invoke themselves at the end. This is efficient with TCO and impractical without (because the stack grows with every iteration).

* You can implement continuations. Imagine if every function took an argument "next", and instead of returning it called "next()". Without TCO, the stack would grow and grow, while with TCO, the old stack frame is torn down when you call "next()".

~~~
cwmma
in your second example you just described most async javascript, through it
tends to be called "callback"

~~~
findjashua
Nope, they're completely different.

In OP's second example, each caller function stays on the stack until its
next() returns, thus the stack keeps growing in size.

In async functions, the caller is thrown off the stack once it hands off the
callback to the event loop. This is why a callback can't return values or
throw exceptions.

~~~
cwmma
* shouldn't throw exceptions

~~~
mattdw
_can 't_ throw exceptions that the caller can catch. If an exception falls in
the forest, and there's no one there to catch it…

------
mck-
Trampolining is a technique to achieve Tail-call Elimination in ES5 [1]

However, I found this to only work in a linearly recursive algorithm (eg
factorial) and not in a bi-directional recursive algorithm (eg tree
construction)

I'm thinking TCO in ES6 might solve this problem? Anyone tried?

[1] [http://raganwald.com/2013/03/28/trampolines-in-
javascript.ht...](http://raganwald.com/2013/03/28/trampolines-in-
javascript.html)

~~~
kevingadd
Another problem with trampolining in ES5 is that it produces GC pressure; not
particularly ideal. TCO in future versions of JavaScript should be a big win,
if only in that area.

~~~
taylodl
I blogged about the advantages/disadvantages of trampolines in JavaScript a
while back. See [http://taylodl.wordpress.com/2013/06/07/functional-
javascrip...](http://taylodl.wordpress.com/2013/06/07/functional-javascript-
tail-call-optimization-and-trampolines/).

I'm preparing to analyze TCO in ES6. I'll do a write up of my analysis.

------
yror10
If you write ruby and want TCO put this at the top of your .rb file:

    
    
        RubyVM::InstructionSequence.compile_option = {
          :tailcall_optimization => true,
          :trace_instruction => false
        }

~~~
eknkc
Why is it optional? What is the benefit of having it disabled by default if
there is a decent implementation?

~~~
anonova
They disabled it for error reporting/debugging reasons. Because you don't need
to create a new stack frame, you eliminate that information in the backtrace.

~~~
steveklabnik
I've seen at least one paper that shows a way to fix this... hm.

------
jbclements
This is absolutely the best news I've had all day. I've been a fierce advocate
of proper tail calling since 1999, and I'm incredibly happy to see it gaining
traction outside of niche functional languages like Racket. Many, many, many
thanks to dherman and jorendorff and all of the others who made this happen.

Now, off to brag about this to my PL class!

------
skybrian
A tail call is basically goto with arguments. You can use it to build
arbitrary extensible state machines.

Like a goto, it's useful but potentially confusing. You shouldn't use it when
a more structured loop construct will work.

~~~
jacquesm
In FP loops are typically frowned upon and then the tail call is presented as
an elegant solution, because it can be optimized into a loop but still look
like a function call with all the goodies a function call normally gives you,
minus the new stack frame.

I'm really not convinced of this, it _looks_ like a function call but it _is_
a loop. If it is a loop under the hood then I'd like to see that loop, in my
mind I always see this 'stackoverflow' neon sign hover over every tail call
(of course it doesn't but years of debugging stuff have ingrained all kinds of
interesting detection habits).

Making it look like a loop would introduce all kinds of syntactical
complexity, and suddenly you'd be re-using your local variables. So that's not
a solution either. But it feels a bit like a kludge.

~~~
mushishi
I'm not so sure that in FP communities recursive call based algorithms are
thought of as elegant solution; rather higher abstractions are desirable (e.g.
map, filter, fold, etc)

~~~
rat87
How do you implement map without tco or loops?(I'm not saying this is
impossible just that I can't think of a way to do it of the top of my head)

~~~
plorkyeran
Basically you want to always use the least powerful tool you can to solve a
problem. When applicable, map is better than foldl not because it's more
concise, but because it tells the reader that the result is a list of the same
length of the input, and the elements of the input are processed
independently. Similarly, foldl is better than raw recursion because it tells
the reader that every element in the input list is being processed in order,
rather than potentially anything happening.

There's nothing wrong with using the more powerful tools when you need them
(such as when implementing the more restricted tools). It's just that needing
them frequently is a sign that you're doing weird things and should take a
second look at your design to make sure there isn't a better way to do things.

------
JBiserkov
So ClojureScript will get TCO before Clojure? (JavaScript would get this
before Java) Interesting times we live in.

~~~
Solarsail
Well, Java does have TCO already, just depending on the JVM you're using. J9
supports it, just not HotSpot.

[http://stackmonthly.com/2010/9/tail-call-
optimization](http://stackmonthly.com/2010/9/tail-call-optimization)

------
platz
How will the affect the debugging of stack frames in javascript programs that
utilize TCO?

~~~
vilhelm_s
Scheme implementations have had to deal with this problem for a long time. (Of
course, one can argue that languages with ordinary while loops have a similar
problem, in that value of previous iterations of the loop get overwritten by
later iterations).

You can alleviate it by hanging on to stack frames for a while. Scheme48 heap-
allocates stack frames, and then relies on garbage collection to reclaim space
from tail-calls, so this happens automatically. And MIT Scheme uses a ring-
buffer to keep around the last 10-or-so frames. (Actually it is even nicer
than that: it uses ringbuffer-of-ringbuffers to keep 10-or-so frames from each
of the last 10-or-so non-tail recursive calls. So when debugging you can pop
up the call stack, and then at each level examine the last couple interations
of the loops done at that level).

------
TheLoneWolfling
...And then there's Python.

(Although, amusingly enough, you can actually hack TCO into Python via
decorator weirdness)

~~~
SoftwareMaven
Not TCO. Tail recursion can be optimized, though. See this[1] discussion at
LtU.

I wish GvR would let TCO through. With TCO, Coffeescript or Clojurescript
might become new favorites.

1\. [http://lambda-the-ultimate.org/node/1331](http://lambda-the-
ultimate.org/node/1331)

