Clojure n'effectue pas lui-même l'optimisation de l'appel final: lorsque vous avez une fonction récursive et que vous souhaitez l'optimiser, vous devez utiliser le formulaire spécial recur
. De même, si vous avez deux fonctions mutuellement récursives, vous ne pouvez les optimiser qu'en utilisant trampoline
.
Le compilateur Scala peut effectuer un TCO pour une fonction récursive, mais pas pour deux fonctions mutuellement récursives.
Chaque fois que j'ai lu ces limitations, elles ont toujours été attribuées à des limitations inhérentes au modèle JVM. Je ne connais pratiquement rien des compilateurs, mais cela me laisse un peu perplexe. Permettez-moi de prendre l'exemple de Programming Scala
. Ici la fonction
def approximate(guess: Double): Double =
if (isGoodEnough(guess)) guess
else approximate(improve(guess))
est traduit en
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
Donc, au niveau du bytecode, on a juste besoin goto
. Dans ce cas, en fait, le travail difficile est effectué par le compilateur.
Quelle installation de la machine virtuelle sous-jacente permettrait au compilateur de gérer le TCO plus facilement?
En passant, je ne m'attendrais pas à ce que les machines réelles soient beaucoup plus intelligentes que la JVM. Néanmoins, de nombreuses langues compilées en code natif, telles que Haskell, ne semblent pas avoir de problèmes pour optimiser les appels en queue (et bien, Haskell peut avoir parfois à cause de la paresse, mais c’est un autre problème).