O Clojure não executa a otimização de chamada de cauda por si só: quando você tem uma função recursiva de cauda e deseja otimizá-la, deve usar o formulário especial recur
. Da mesma forma, se você tiver duas funções recursivas mutuamente, poderá otimizá-las apenas usando trampoline
.
O compilador Scala é capaz de executar o TCO para uma função recursiva, mas não para duas funções recursivas mutuamente.
Sempre que li sobre essas limitações, elas sempre foram atribuídas a alguma limitação intrínseca ao modelo da JVM. Não sei praticamente nada sobre compiladores, mas isso me deixa um pouco confuso. Deixe-me pegar o exemplo de Programming Scala
. Aqui a função
def approximate(guess: Double): Double =
if (isGoodEnough(guess)) guess
else approximate(improve(guess))
é traduzido para
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
Então, no nível do bytecode, é preciso apenas goto
. Nesse caso, de fato, o trabalho árduo é realizado pelo compilador.
Que facilidade da máquina virtual subjacente permitiria ao compilador lidar com o TCO mais facilmente?
Como uma observação lateral, eu não esperaria que as máquinas reais fossem muito mais inteligentes que a JVM. Ainda assim, muitos idiomas que são compilados com código nativo, como Haskell, parecem não ter problemas com a otimização de chamadas de cauda (bem, o Haskell pode ter algumas vezes devido à preguiça, mas esse é outro problema).