I have lisp in javascript which is similar to scheme. It can be used with lexical and dynamic scopes. I was not sure how dynamic scope works and it's seems ok but this code don't work when scope is dynamic:
(define Y
(lambda (h)
((lambda (x) (x x))
(lambda (g)
(h (lambda args (apply (g g) args)))))))
(define (trampoline f)
(lambda args
(let ((result (apply f args)))
(while (eq? (type result) "function")
(set result (result)))
result)))
(define (! n)
((trampoline (Y (lambda (f)
(lambda (n acc)
(if (== n 0)
acc
(lambda ()
(f (- n 1) (* n acc)))))))) n 1))
(print (! 1000))
it works fine when scope is lexical. Should this code work when scope is dynamic? Right now it just do nothing and I don't know why but wanted to be sure that this code should work before I start debugging and make my dynamic scope break because of this.
My lisp with demo is here https://jcubic.github.io/lips/ but the code that make this work for lexical scope is not yet published so it will not work. (it's in devel branch and I can create codepen demo with it or using Stack Snippet).
I don't see how trampoline
can work with dynamic scoping.
Simplified evaluation:
(define Y ...)
Now Y
is bound (to some value).
(define (trampoline f)
(lambda args
(let ((result (apply f args)))
...)))
Now trampoline
is bound to (lambda (f) (lambda args (let ((result (apply f args))) ...)))
.
(define (! n)
((trampoline ...) n 1))
Now !
is bound to (lambda (n) ((trampoline ...) n 1))
.
(print (! 1000))
We evaluate the inner call first, so we need to resolve !
and apply it to 1000
.
By the definition of !
above we bind n
to 1000
and evaluate ((trampoline ...) n 1)
.
We need to call trampoline
. By the definition of trampoline
above, we bind f
to ...
and return (lambda args (let ((result (apply f args))) ...))
.
We return from trampoline
and undo the binding of f
.
We now need to evaluate ((lambda args (let ((result (apply f args))) ...)) n 1)
(applying the return value of trampoline
to n
and 1
).
n
is currently bound to 1000
, so this expression becomes ((lambda args (let ((result (apply f args))) ...)) 1000 1)
. To perform the call call we bind args
to (1000 1)
.
Now we need to evaluate (apply f args)
(to bind the result to result
as part of let
). apply
is in the standard library. args
was just bound to (1000 1)
above. But there is no binding for f
.
At this point we should throw an error: The only binding of f
we've seen so far was during the call to trampoline
(where f
was a parameter). But that call has already returned and the binding was removed, so f
is unbound.
Live demo (using a Perl version of your code where all bindings are made dynamic manually): https://ideone.com/DWjwBj
It blows up as predicted: Can't use an undefined value as a subroutine reference
for the line local $result = $f->(@args);
because $f
is unbound.
If you change all bindings to lexical (replace all occurrences of local
by my
), $fac->(5)
returns 120
as expected.