Consider following definitions. I am using Racket.
(define fact/k
(lambda (n k)
(cond
((zero? n) (call/cc (lambda (f) (k f))))
(else (* n (fact/k (sub1 n) k))))))
(define five (call/cc (lambda (k) (fact/k 5 k))))
Now if it invoked as follows
(five 1)
It gives out nothing. Having done that if five is invoked directly it gives 120.
$five
120
But if I retry (five 1) it fails saying 120 is not a procedure.
I understand that initially five points to the continuation captured at (zero? n) base case. But I am not sure how above behavior can be explained.
Another run with different parameter
$ (five 4)
$ five
480
NB: You code is not allowed in the last version of #!racket without changing the language opetion. Go in Language > Choose language and uncheck "Enforce constant definitions". You'll loose some optimization wit this unchecked.
five
is a continuation similar to(lambda (n) (set! five (* 5 4 3 2 1 n)))
and by using two continuations you mamage to redefinefive
after one call. Evaluatingfive
afterwards revieals the answer of(* 5 4 3 2 1 1)
when the argument is1
and(* 5 4 3 2 1 4)
when it's4
.Variables in Scheme/Racket does not have type but values have. That means a variable
five
can be a procedure/continuation first and just a number second. That is what you see happening.EDIT To recap I rename a little:
Considering marked line #1. In
fact/k
the default case is run for n 5..1 so you can substitute the line with(define five (call/cc (lambda (five-k) (* 5 4 3 2 1 (fact/k 0 five-k)))))
. Instead offact/k
returning a number it uses the continuation call with and what it passes as value is the continuation from withinfact/k
calledf
. If you then evaluatefive
on #2 you get thef
continuation. Callingf
with a numeric argument becomes the last answer to the calculation that never happened since we aborted it and returned f instead. In #3 you callfive
with 4 as argument. Continuations is time traveling so you're now back to(define five (call/cc (lambda (five-k) (* 5 4 3 2 1 (fact/k 0 five-k)))))
where you just know that(fact/k 0 five-k)
evaluated to4
. What happens next is that(* 5 4 3 2 1 4)
becomes480``and that is set to
fivesince setting the variable is done *after the calculation of it's value*. On line #4 you verify that
five` has indeed been changed from a continuation to a value. It's value is changed to one of a totally different type. You cannot call a number.In DrRacket you can hit the debug button and step through it. I recommend you try it out.