Thu 26 June 2025

Tail Recursion is a Code Smell

Posted by Al Sweigart in misc   

Here are some of the most widely-used language implementations that do not perform general tail-call optimization by default:

  • Python (CPython) – Stock CPython does not eliminate tail calls, so deep tail-recursive functions will still blow the call stack (Wikipedia).
  • Java (HotSpot JVM) – The JVM specification doesn’t require tail-call elimination, and HotSpot does not perform it by default (Stack Overflow).
  • JavaScript engines (V8 in Chrome/Node.js & SpiderMonkey in Firefox) – Although ES6 says proper tail calls should be supported, V8 and SpiderMonkey both lack PTC/TCO (Reddit).
  • Ruby MRI (Matz’s Interpreter) – MRI doesn’t do TCO unless you explicitly enable it via RubyVM::InstructionSequence (Stack Overflow).
  • PHP (Zend Engine) – The default PHP interpreter doesn’t optimize away tail calls (Stack Overflow).
  • Perl 5 – Perl’s core runtime doesn’t perform tail-call elimination (Stack Overflow).
  • Go (gc compiler) – The Go toolchain does not implement general tail-call elimination (Stack Overflow).
  • Rust (rustc) – Rust’s optimizer might sometimes elide tail calls, but TCO is neither guaranteed nor broadly applied (Stack Overflow).
  • C# (csc/.NET CLR) – While the CLR supports a tail-call opcode, the C# compiler doesn’t emit it by default (F# does) (Stack Overflow).

In contrast, purely functional runtimes (Scheme, Haskell, Erlang, etc.) and languages targeting the BEAM VM (Elixir, Erlang) almost always guarantee TCO.


Check out other books by Al Sweigart, free online or available for purchase:

...and other books as well! Or register for the online video course. You can also donate to or support the author directly.