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 іноді може виникнути через лінь, але це інша проблема).