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:
Now
Y
is bound (to some value).Now
trampoline
is bound to(lambda (f) (lambda args (let ((result (apply f args))) ...)))
.Now
!
is bound to(lambda (n) ((trampoline ...) n 1))
.We evaluate the inner call first, so we need to resolve
!
and apply it to1000
.By the definition of
!
above we bindn
to1000
and evaluate((trampoline ...) n 1)
.We need to call
trampoline
. By the definition oftrampoline
above, we bindf
to...
and return(lambda args (let ((result (apply f args))) ...))
.We return from
trampoline
and undo the binding off
.We now need to evaluate
((lambda args (let ((result (apply f args))) ...)) n 1)
(applying the return value oftrampoline
ton
and1
).n
is currently bound to1000
, so this expression becomes((lambda args (let ((result (apply f args))) ...)) 1000 1)
. To perform the call call we bindargs
to(1000 1)
.Now we need to evaluate
(apply f args)
(to bind the result toresult
as part oflet
).apply
is in the standard library.args
was just bound to(1000 1)
above. But there is no binding forf
.At this point we should throw an error: The only binding of
f
we've seen so far was during the call totrampoline
(wheref
was a parameter). But that call has already returned and the binding was removed, sof
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 linelocal $result = $f->(@args);
because$f
is unbound.If you change all bindings to lexical (replace all occurrences of
local
bymy
),$fac->(5)
returns120
as expected.