Clojure no realiza la optimización de llamadas de cola por sí solo: cuando tiene una función recursiva de cola y desea optimizarla, debe usar la forma especial recur
. Del mismo modo, si tiene dos funciones recursivas entre sí, puede optimizarlas solo mediante el uso trampoline
.
El compilador Scala puede realizar TCO para una función recursiva, pero no para dos funciones recursivas mutuamente.
Siempre que leí sobre estas limitaciones, siempre se les atribuyó alguna limitación intrínseca al modelo JVM. No sé casi nada sobre compiladores, pero esto me desconcierta un poco. Déjame tomar el ejemplo de Programming Scala
. Aquí la función
def approximate(guess: Double): Double =
if (isGoodEnough(guess)) guess
else approximate(improve(guess))
se traduce a
0: aload_0
1: astore_3
2: aload_0
3: dload_1
4: invokevirtual #24; //Method isGoodEnough:(D)Z
7: ifeq
10: dload_1
11: dreturn
12: aload_0
13: dload_1
14: invokevirtual #27; //Method improve:(D)D
17: dstore_1
18: goto 2
Entonces, a nivel de bytecode, uno solo necesita goto
. En este caso, de hecho, el compilador realiza el trabajo duro.
¿Qué facilidad de la máquina virtual subyacente permitiría al compilador manejar el TCO más fácilmente?
Como nota al margen, no esperaría que las máquinas reales fueran mucho más inteligentes que la JVM. Aún así, muchos lenguajes que se compilan en código nativo, como Haskell, no parecen tener problemas para optimizar las llamadas de cola (bueno, Haskell a veces puede deberse a la pereza, pero ese es otro problema).