Я не понимаю, как trampoline
может работать с динамической областью видимости.
Упрощенная оценка:
(define Y ...)
Теперь Y
привязано (к некоторому значению).
(define (trampoline f)
(lambda args
(let ((result (apply f args)))
...)))
Теперь trampoline
связан с (lambda (f) (lambda args (let ((result (apply f args))) ...)))
.
(define (! n)
((trampoline ...) n 1))
Теперь !
связан с (lambda (n) ((trampoline ...) n 1))
.
(print (! 1000))
Сначала мы оцениваем внутренний вызов, поэтому нам нужно разрешить !
и применить его к 1000
.
По определению !
выше мы связываем n
с 1000
и оцениваем ((trampoline ...) n 1)
.
Нам нужно позвонить trampoline
. По определению trampoline
выше мы связываем f
с ...
и возвращаем (lambda args (let ((result (apply f args))) ...))
.
Мы возвращаемся с trampoline
и отменяем привязку f
.
Теперь нам нужно оценить ((lambda args (let ((result (apply f args))) ...)) n 1)
(применяя возвращаемое значение от trampoline
к n
и 1
).
n
в настоящее время связан с 1000
, поэтому это выражение становится ((lambda args (let ((result (apply f args))) ...)) 1000 1)
. Чтобы выполнить вызов, мы связываем args
с (1000 1)
.
Теперь нам нужно оценить (apply f args)
(чтобы связать результат с result
как часть let
). apply
находится в стандартной библиотеке. args
был просто связан с (1000 1)
выше. Но для f
.
нет привязки
В этот момент мы должны выдать ошибку: единственная привязка f
, которую мы видели до сих пор, была во время вызова trampoline
(где f
был параметром). Но этот вызов уже вернулся, и привязка была удалена, поэтому f
не связан.
Демонстрация в реальном времени (с использованием Perl-версии вашего кода, где все привязки сделаны динамическими вручную): https://ideone.com/DWjwBj
Он взрывается, как и предсказывалось: Can't use an undefined value as a subroutine reference
для линии local $result = $f->(@args);
, потому что $f
не связан.
Если вы измените все привязки на лексические (замените все вхождения local
на my
), $fac->(5)
вернет 120
, как и ожидалось.