I use racket and I got the result 4 for following simple code:
(let/cc done
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3))
and I was going to execute this code step-by-step.
First, I changed the first let/cc into the form of call/cc like below:
(call/cc (λ (done)
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3)))
Of course, this produces 4 also.
Second, since I found the mechanism of call/cc in the internet which says call/cc do following 4 steps:
Captures the current continuation.
Constructs a function C that takes one argument, and applies the current continuation with that argument value.
Passes this function as an argument to expr --- i.e., it invokes (expr C).
Returns the result of evaluating (expr C), unless expr calls C, in which case the value that is passed to C is returned.
Thus, I followed above steps for the first call/cc like:
Current continuation is an identity.
C refers (λ (x) x).
Since expr is (λ (done) ((let/cc esc (done (+ 1 (let/cc k (esc k))))) 3)), (expr C) is:
((λ (done)
((let/cc esc
(done (+ 1 (let/cc k
(esc k)))))
3))
(λ (x) x))
To return the result value of above code, I execute above in racket.
But, above code (modified by me) is not executed and produces an error:
> application: not a procedure;
>
> expected a procedure that can be applied to arguments
>
> given: 4
>
> arguments...:
>
> 3
Please what I did wrong. I'm confusing the concept of continuation. Thanks.
When the interpreter sees a call/cc even the interpreters that doesn't do CPS does it with that subtree. Your code would look something like this:
((λ (done)
((λ (esc)
((λ (k) (esc k))
(λ (r) (k+ done 1 r))))
(λ (v) (v 3))))
values)
; k+ implementation (+, but CPS)
(define (k+ k . args)
(k (apply + args)))
Continuations are not just closures (functions). They also perform a jump to their defining location in code. You have to perform the CPS transformation in full to try evaluating the resulting expression in Scheme interpreter. That expression will only contain lambdas and no continuations (in the sense of call/cc (1)).
The expression that you tried mixes them both - it defines done as simple lambda-defined function, but it is still used in the nested context as a continuation.
(1) (another source of confusion is calling the function arguments in the continuation-passing style "continuations". they are not "true" continuations; they are simple functions "to be called" in this or that eventuality, so colloquially they are also referred to as "continuations" although "contingencies" or even "handlers" could be better.)
See also another example of call/cc code translation.
Following that approach, translating your Scheme code into Common Lisp, we get:
;; (let/cc done
;; ((let/cc esc
;; (done (+ 1 (let/cc k
;; (esc k)))))
;; 3))
(prog (retval done arg1 func esc arg2 k arg3 arg4)
(setq done (lambda (x) (setq retval x) (go DONE))) ; 3
(setq arg1 3) ; 5
(setq esc (lambda (x) (setq func x) (go ESC))) ; 8
(setq arg3 1) ; 10
(setq k (lambda (x) (setq arg4 x) (go K))) ; 12
(setq arg4 (funcall esc k)) ; 13
K ; 11 continuation K
(setq arg2 (+ arg3 arg4)) ; 9
(setq func (funcall done arg2)) ; 7
ESC ; 6 continuation ESC
(setq retval (funcall func arg1)) ; 4
DONE ; 2 continuation DONE
(return retval)) ; 1
which indeed returns 4 (the lines of code are numbered in order as they are written, during the translation).
Related
Why this code
(let ([cc #f]
[pr (make-continuation-prompt-tag 'pr)])
(call-with-continuation-prompt
(λ () (displayln
(+ 2 (call-with-current-continuation
(λ (k) (set! cc k) 1)
pr))))
pr)
(cc 4))
(on Racket v7.5) raise exception?:
3
; continuation application: no corresponding prompt in the current continuation
; Context:
; /usr/share/racket/collects/racket/repl.rkt:11:26
While same code with default tag works as expected:
(let ([cc #f])
(call-with-continuation-prompt
(λ ()
(displayln (+ 2 (call-with-current-continuation
(λ (k) (set! cc k) 1))))))
(cc 4))
3
6
And same (as the first snippet) code with composable continuation
(let ([cc #f]
[pr (make-continuation-prompt-tag 'pr)])
(call-with-continuation-prompt
(λ () (displayln
(+ 2 (call-with-composable-continuation
(λ (k) (set! cc k) 1)
pr))))
pr)
(cc 4))
works as expected too:
3
6
What is wrong with the first snippet? Or what is wrong with my understanding?
From the docs for call-with-current-continuation:
If the continuation argument to proc is ever applied, then it removes the portion of the current continuation up to the nearest prompt tagged by prompt-tag (not including the prompt; if no such prompt exists, the exn:fail:contract:continuation exception is raised), ....
In your first example, when you apply cc, there is no prompt for pr in the context ("on the stack") when the application occurs, so it raises an exception.
The second example works because there is always a prompt for the default tag.
The third example works because call-with-composable-continuation creates a continuation procedure that does not abort the current continuation, so there is no precondition for its application. (That's part of why it's considered a "composable" continuation.)
If it helps, here is approximately how call/cc can be defined in terms of the abort-current-continuation and call-with-compposable-continuation. (Warning: I haven't tested this, so there may be bugs.) In the type annotations below, I use the following conventions: P is the result type associated with a prompt tag and A is the result type of a call/cc or call/comp call, which is also the type of the continuation's argument. ⊥ is the empty type; it effectively means "doesn't return".
;; call-with-continuation-prompt : (-> P) PromptTag[P] -> P
;; Only allows default abort handler!
;; abort-current-continuation : PromptTag[P] (-> P) -> ⊥
;; Assumes the default abort handler!
;; call-with-composable-continuation : ((A -> P) -> A) PromptTag[P] -> A
;; call/cc : ((A -> ⊥) -> A) PromptTag[P] -> A
(define (call/cc proc tag)
(call-with-composable-continuation
(lambda (ck) ;; ck : A -> P
;; k : A -> ⊥
(define (k v)
(abort-current-continuation tag
(lambda () (ck v))))
(proc k))
tag))
This definition doesn't account for how call/cc actually interacts with dynamic-wind, it doesn't work with custom prompt handlers, and it doesn't account for multiple return values (which correspond to multiple continuation arguments), but it should give you a rough idea of what call/cc is doing. In particular, the call to abort-current-continuation requires that the current continuation has a prompt tagged with tag.
I'm trying to understand call/cc operator in Scheme. I'm planing of implementing this in my JavaScript lisp. This is my simple code:
(letrec ((x 0)
(f (lambda (r)
(set! x r)
(display (r 20))
(display "10"))))
(display (call/cc f))
(x "30"))
I tough that it should print 20 then 30 then 10. But it create infinite loop (it keep printing 30). How this code should look like to display 3 values, call display 3 times?
Should it be possible to create loops that don't consume stack with continuations?
I've found some example on stack overflow but this one don't work at all:
(define x 0) ; dummy value - will be used to store continuation later
(+ 2 (call/cc (lambda (cc)
(set! x cc) ; set x to the continuation cc; namely, (+ 2 _)
3))) ; returns 5
(x 4) ; returns 6
it freezes the guile interpreter with 100% CPU and it look it waiting for input.
Does you lisp implementation transform user code to continuation passing style? In that case it is easy peasy. call/cc is this:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
Looking at your first code I think it becomes something like this:
((lambda (x k)
((lambda (f k)
(call/cc& f (lambda (v) ; continuation a
(display& v (lambda (_) ; continuation b
(x "30" k))))))
(lambda (r k)
(set!& x r (lambda (_) ; continuation c
(r 20 (lambda (v) ; continuation d
(display& v (lambda (_) ; continuation e
(display& "10" k))))))))
k)
0
halt)
Here is whats happening:
We make x and f
call/cc& calls f
x is set to r (continuation a)
r gets called with 20 as value
continuation c is ignore, instead continuation a is called with 20
20 gets displayed, then continuation b gets called
b calls x with "30"
continuation k is ignored, instead continuation a is called with 30
30 gets displayed, then continuation b gets called
go to "b calls x with "30" 3 lines up and continue
So print "20", then "30" forever seems to be the correct result for this code. It's important to notice that it will never display "10" since it calls r and passes the continuation but it gets circumvented to call/cc original continution which is continuation a.
As for implementations. Before it was quite common for all Scheme implementations to just transform the code to continuation passing style, but today it's more common to only do the parts which are required. Eg. Ikarus does not do CPS, but in order for call/cc to work it needs to do it until the next continuation prompt.
It's probably better to look at call/cc without mutation in the beginning. eg.
(+ 2 (call/cc (lambda (exit)
(+ 3 (* 5 (exit 11))))))
Now this turns into:
(call/cc& (lambda (exit k)
(exit 11 (lambda (v)
(*& 5 v (lambda (v)
(+& 3 v k))))))
(lambda (v)
(+& 2 v repl-display)))
Now we know exit gets called and thus this whole thing turns into:
((lambda (v) (+& 2 v repl-display)) 11)
Which displays 13. Now having the continuation as last argument looks good on paper. In an implementation that wants to support varargs it's probably best that the continuation is the first argument.
All continuations are tail calls and thus the stack is never grown. In fact if full CPS is used you never have to return ever. Everything interesting is always passed to the next call until the program halts.
I am having problems with this
e.g. i have
(define (mypow x) (* x x))
and I need to eval expressions from given list. (I am writing a simulator and I get a sequence of commands in a list as an argument)
I have already read that R5RS standard needs to include in function eval as second arg (scheme-report-environment 5), but still I am having issues with this.
This works (with standard function):
(eval '(sqrt 5) (scheme-report-environment 5))
but this does not:
(eval '(mypow 5) (scheme-report-environment 5))
It says:
../../../../../../usr/share/racket/collects/racket/private/kw.rkt:923:25: mypow: undefined;
cannot reference undefined identifier
Eventhough simply called mypow in prompt returns:
#<procedure:mypow>
Any advice, please? (btw I need to use R5RS)
(scheme-report-environment 5) returns all the bindings that are defined in the R5RS Scheme standard and not any of the user defined ones. This is by design. You will never be able to do what you want using this as the second parameter to eval.
The report mentions (interaction-environment) which is optional. Thus you have no guarantee the implementation has it, but it will have all the bindings from (scheme-report-environment 5)
For completeness there is (null-environment 5) which only has the bindings for the syntax. eg. (eval '(lambda (v) "constan) (null-environment 5)) works, but (eval '(lambda (v) (+ 5 v)) (null-environment 5)) won't since + is not in the resulting procedures closure.
Other ways to get things done
Usually you could get away without using eval alltogether. eval should be avoided at almost all costs. The last 16 years I've used eval deliberately in production code twice.
By using a thunk instead of data:
(define todo
(lambda () ; a thunk is just a procedure that takes no arguments
(mypow 5))
(todo) ; ==> result from mypow
Now imagine you have a list of operations you'd like done instead:
(define ops
`((inc . ,(lambda (v) (+ v 1))) ; notice I'm unquoting.
(dec . ,(lambda (v) (- v 1))) ; eg. need the result of the
(square . ,(lambda (v) (* v v))))) ; evaluation
(define todo '(inc square dec))
(define with 5)
;; not standard, but often present as either fold or foldl
;; if not fetch from SRFI-1 https://srfi.schemers.org/srfi-1/srfi-1.html
(fold (lambda (e a)
((cdr (assq e ops)) a))
with
todo)
; ==> 35 ((5+1)^2)-1
The following example involves jumping into continuation and exiting out. Can somebody explain the flow of the function. I am moving in a circle around continuation, and do not know the entry and exit points of the function.
(define (prod-iterator lst)
(letrec ((return-result empty)
(resume-visit (lambda (dummy) (process-list lst 1)))
(process-list
(lambda (lst p)
(if (empty? lst)
(begin
(set! resume-visit (lambda (dummy) 0))
(return-result p))
(if (= 0 (first lst))
(begin
(call/cc ; Want to continue here after delivering result
(lambda (k)
(set! resume-visit k)
(return-result p)))
(process-list (rest lst) 1))
(process-list (rest lst) (* p (first lst))))))))
(lambda ()
(call/cc
(lambda (k)
(set! return-result k)
(resume-visit 'dummy))))))
(define iter (prod-iterator '(1 2 3 0 4 5 6 0 7 0 0 8 9)))
(iter) ; 6
(iter) ; 120
(iter) ; 7
(iter) ; 1
(iter) ; 72
(iter) ; 0
(iter) ; 0
Thanks.
The procedure iterates over a list, multiplying non-zero members and returning a result each time a zero is found. Resume-visit stores the continuation for processing the rest of the list, and return-result has the continuation of the call-site of the iterator. In the beginning, resume-visit is defined to process the entire list. Each time a zero is found, a continuation is captured, which when invoked executes (process-list (rest lst) 1) for whatever value lst had at the time. When the list is exhausted, resume-visit is set to a dummy procedure. Moreover, every time the program calls iter, it executes the following:
(call/cc
(lambda (k)
(set! return-result k)
(resume-visit 'dummy)))
That is, it captures the continuation of the caller, invoking it returns a value to the caller. The continuation is stored and the program jumps to process the rest of the list.
When the procedure calls resume-visit, the loop is entered, when return-result is called, the loop is exited.
If we want to examine process-list in more detail, let's assume the list is non-empty. Tho procedure employs basic recursion, accumulating a result until a zero is found. At that point, p is the accumulated value and lst is the list containing the zero. When we have a construction like (begin (call/cc (lambda (k) first)) rest), we first execute first expressions with k bound to a continuation. It is a procedure that when invoked, executes rest expressions. In this case, that continuation is stored and another continuation is invoked, which returns the accumulated result p to the caller of iter. That continuation will be invoked the next time iter is called, then the loop continues with the rest of the list. That is the point with the continuations, everything else is basic recursion.
What you need to keep in mind is that, a call to (call/cc f) will apply the function f passed as argument to call/cc to the current continuation. If that continuation is called with some argument a inside the function f, the execution will go to the corresponding call to call/cc, and the argument a will be returned as the return value of that call/cc.
Your program stores the continuation of "calling call/cc in iter" in the variable return-result, and begins processing the list. It multiplies the first 3 non-zero elements of the list before encountering the first 0. When it sees the 0, the continuation "processing the list element 0" is stored in resume-visit, and the value p is returned to the continuation return-result by calling (return-result p). This call will make the execution go back to the call/cc in iter, and that call/cc returns the passed value of p. So you see the first output 6.
The rest calls to iter are similar and will make the execution go back and forth between such two continuations. Manual analysis may be a little brain-twisting, you have to know what the execution context is when a continuation is restored.
You could achieve the same this way:
(define (prod-iter lst) (fold * 1 (remove zero? lst)))
... even though it could perform better by traversing only once.
For continuations, recall (pun intended) that all call/cc does is wait for "k" to be applied this way:
(call/cc (lambda (k) (k 'return-value)))
=> return-value
The trick here is that you can let call/cc return its own continuation so that it can be applied elsewhere after call/cc has returned like this:
;; returns twice; once to get bound to k, the other to return blah
(let ([k (call/cc (lambda (k) k))]) ;; k gets bound to a continuation
(k 'blah)) ;; k returns here
=> blah
This lets a continuation return more than once by saving it in a variable. Continuations simply return the value they are applied to.
Closures are functions that carry their environment variables along with them before arguments get bounded to them. They are ordinary lambdas.
Continuation-passing style is a way to pass closures as arguments to be applied later. We say that these closure arguments are continuations. Here's half of the current code from my sudoku generator/solver as an example demonstrating how continuation-passing style can simplify your algorithms:
#| the grid is internally represented as a vector of 81 numbers
example: (make-vector 81 0)
this builds a list of indexes |#
(define (cell n) (list (+ (* (car 9) (cadr n))))
(define (row n) (iota 9 (* n 9)))
(define (column n) (iota 9 n 9))
(define (region n)
(let* ([end (+ (* (floor-quotient n 3) 27)
(* (remainder n 3) 3))]
[i (+ end 21)])
(do ([i i
(- i (if (zero? (remainder i 3)) 7 1))]
[ls '() (cons (vector-ref *grid* i) ls)])
((= i end) ls))))
#| f is the continuation
usage examples:
(grid-ref *grid* row 0)
(grid-set! *grid* region 7) |#
(define (grid-ref g f n)
(map (lambda (i) (vector-ref g i)) (f n)))
(define (grid-set! g f n ls)
(for-each (lambda (i x) (vector-set! g i x))
(f n) ls))
Does Scheme or do any dialects of scheme have a kind of "self" operator so that anonymous lambdas can recur on themselves without doing something like a Y-combinator or being named in a letrec etc.
Something like:
(lambda (n)
(cond
((= n 0) 1)
(else (* n (self (- n 1)))))))
No. The trouble with the "current lambda" approach is that Scheme has many hidden lambdas. For example:
All the let forms (including let*, letrec, and named let)
do (which expands to a named let)
delay, lazy, receive, etc.
To require the programmer to know what the innermost lambda is would break encapsulation, in that you'd have to know where all the hidden lambdas are, and macro writers can no longer use lambdas as a way to create a new scope.
All-round lose, if you ask me.
There is a tradition of writing “anaphoric” macros that define special names in the lexical scope of their bodies. Using syntax-case, you can write such a macro on top of letrec and lambda. Note that the definition below is as hygienic as possible considering the specification (in particular, invisible uses of alambda will not shadow self).
;; Define a version of lambda that binds the
;; anaphoric variable “self” to the function
;; being defined.
;;
;; Note the use of datum->syntax to specify the
;; scope of the anaphoric identifier.
(define-syntax alambda
(lambda (stx)
(syntax-case stx ()
[(alambda lambda-list . body)
(with-syntax ([name (datum->syntax #'alambda 'self)])
#'(letrec ([name (lambda lambda-list . body)])
name))])))
;; We can define let in terms of alambda as usual.
(define-syntax let/alambda
(syntax-rules ()
[(_ ((var val) ...) . body)
((alambda (var ...) . body) val ...)]))
;; The let/alambda macro does not shadow the outer
;; alambda's anaphoric variable, which is lexical
;; with regard to the alambda form.
((alambda (n)
(if (zero? n)
1
(let/alambda ([n-1 (- n 1)])
(* (self n-1) n))))
10)
;=> 3628800
Most people avoid anaphoric operators since they make the structure of the code less recognizable. In addition, refactoring can introduce problems rather easily. (Consider what happens when you wrap the let/alambda form in the factorial function above in another alambda form. It's easy to overlook uses of self, especially if you're not reminded of it being relevant by having to type it explicitly.) It is therefore generally preferable to use explicit names. A “labeled” version of lambda that allows this can be defined using a simple syntax-rules macro:
;; Define a version of lambda that allows the
;; user to specifiy a name for the function
;; being defined.
(define-syntax llambda
(syntax-rules ()
[(_ name lambda-list . body)
(letrec ([name (lambda lambda-list . body)])
name)]))
;; The factorial function can be expressed
;; using llambda.
((llambda fac (n)
(if (zero? n)
1
(* (fac (- n 1)) n)))
10)
;=> 3628800
I have found a way using continuations to have anonymous lambdas call themselves and then using Racket macros to disguise the syntax so the anonymous lambda appears to have a "self" operator. I don't know if this solution is possible in other versions of Scheme since it depends on the Call-with-composable-continuation function of racket and the Macro to hide the syntax uses syntax parameters.
The basic idea is this, illustrated with the factorial function.
( (lambda (n)
(call-with-values
(lambda () (call-with-composable-continuation
(lambda (k) (values k n))))
(lambda (k n)
(cond
[(= 0 n) 1]
[else (* n (k k (- n 1)))])))) 5)
The continuation k is the call to the anonymous factorial function, which takes two arguments, the first being the continuation itself. So that when in the body we execute (k k N) that is equivalent to the anonymous function calling itself (in the same way that a recursive named lambda would do).
We then disguise the underlying form with a macro. Rackets syntax-parameters allow the transformation of (self ARGS ...) to (k k ARGS ... )
so we can have:
((lambda-with-self (n)
(cond
[(= 0 n) 0]
[(= 1 n) 1]
[else (* n (self (- n 1)))])) 5)
The complete Racket program to do this is:
#lang racket
(require racket/stxparam) ;required for syntax-parameters
( define-syntax-parameter self (λ (stx) (raise-syntax-error #f "not in `lambda-with-self'" stx)))
(define-syntax-rule
(lambda-with-self (ARG ... ) BODY ...)
(lambda (ARG ...)
(call-with-values
(lambda ()(call/comp (lambda (k) (values k ARG ...))))
(lambda (k ARG ...)
(syntax-parameterize ([self (syntax-rules ( )[(self ARG ...) (k k ARG ...)])])
BODY ...)))))
;Example using factorial function
((lambda-with-self (n)
(cond
[(= 0 n) 0]
[(= 1 n) 1]
[else (* n (self (- n 1)))])) 5)
This also answers my previous question about the differences between the different kinds of continuations.
Different kinds of continuations in Racket
This only works because unlike call-with-current-continuation, call-with-composable-continuation doesn't abort back to a continuation prompt but invokes the continuation at the place it was invoked.