Tail Call Optimization in Elixir & Erlang – not as efficient and important as you probably think

Some time ago I read something, I thik it was written by @rvirding but I’m not really sure about it, that aimed as well at this discussion.

It TL;DR was roughly: When building a list which order matters, it does not really matter if you build it “on the stack” or in an accumulator which you reverse afterwards. One of both might kill the stack, the other one just will stress your GC. But whenever order of the resulting list does not matter anymore or when you reduce to a single value, an accumulater in a tail recursive function is way better, because it is faster and does not stress the stack that much.

Also I do have an important nitpick about nomenclature:

We don’t write TCO’d functions. Tail Call Optimisation is an compile time optimisation done by a compiler. This particular optimisation does mean to rewrite a tail recursive function in a way that it can get compiled into a loop/direct jump in the targetted language instead of a function call. Sice we don’t push and pop on a/the stack when looping we save up some cycles there, as well as we do not need to grow the stack by 20k frames which are more or less the same.

There are some discussions around about the fact if TCO is really a good thing so in many compilers (not necessarily for erlang or elixir) you can switch off TCO (as well as other optimisations). Some languages (-> Python) do even refuse to implement TCO in the referential implementation because of these concerns: When looping instead of calling, then there are steps missing in the stacktrace and you can’t know how deep you actually are before getting that exception thrown in the face.

1 Like