Clojure не выполняет оптимизацию хвостового вызова самостоятельно: если у вас есть хвостовая рекурсивная функция и вы хотите оптимизировать ее, вы должны использовать специальную форму recur
. Точно так же, если у вас есть две взаимно рекурсивные функции, вы можете оптимизировать их только с помощью trampoline
.
Компилятор Scala может выполнять TCO для рекурсивной функции, но не для двух взаимно рекурсивных функций.
Всякий раз, когда я читал об этих ограничениях, они всегда приписывались некоторым ограничениям, свойственным модели JVM. Я почти ничего не знаю о компиляторах, но это меня немного озадачивает. Позвольте мне взять пример с Programming Scala
. Здесь функция
def approximate(guess: Double): Double =
if (isGoodEnough(guess)) guess
else approximate(improve(guess))
переводится на
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
Итак, на уровне байт-кода нужно просто goto
. В этом случае, фактически, тяжелая работа выполняется компилятором.
Какие средства базовой виртуальной машины позволят компилятору легче обрабатывать TCO?
Как примечание стороны, я не ожидал бы, что фактические машины будут намного умнее, чем JVM. Тем не менее, многие языки, которые компилируются в нативный код, такие как Haskell, похоже, не имеют проблем с оптимизацией хвостовых вызовов (ну, иногда у Haskell может быть лень, но это другая проблема).