How to use tagged prompts with call/cc in Racket? - scheme

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.

Related

How do I get a function's name as a symbol?

I am trying to define a function func->symbol that takes a function and returns its name as a symbol. For example:
(define (pythagoras a b)
(sqrt (+ (* a a) (* b b))))
;; #1
(func->symbol pythagoras) ; Returns: 'pythagoras
;; #2
(func->symbol (if #t pythagoras sqrt)) ; Returns: 'pythagoras
;; #3
(let ((f (if #t pythagoras sqrt)))
(func->symbol f)) ; Returns: 'pythagoras
;; #4
(let ((f (if #t pythagoras sqrt)))
(let ((g f))
(func->symbol g))) ; Returns: 'pythagoras
This is a follow-up question on How do I get a definition's name as a symbol? which only deals with case #1. For case #1, a simple macro def->symbol is sufficient:
(define-syntax def->symbol
(syntax-rules ()
((_ def) 'def)))
However, this macro definition does not pass cases #2, #3, #4. Is it possible to define func->symbol, or is Scheme not expressive enough for this?
In Racket, in many cases, you can get a function's name using object-name. But it is probably a bad idea to rely on this result for anything other than debugging.
Perhaps it's worth an answer which shows why this is not possible in any language with first-class functions.
I'll define what I mean by a language having first-class functions (there are varying definitions).
Functions can be passed as arguments to other functions, and returned as values from them.
Functions can be stored in variables and other data structures.
There are anonymous functions, or function literals.
Scheme clearly has first-class functions in this sense. Now consider this code:
(define a #f)
(define b #f)
(let ((f (lambda (x)
(+ x 1))))
(set! a f)
(set! b f))
Let's imagine there is a function-name function, which, given a function, returns its name. What should (function-name a) return?
Well, the answer is that there's simply no useful value it can return (in Racket, (object-name a) returns f, but that's clearly exposing implementation details which might be useful for debugging but would be very misleading as a return value for a function-name procedure.
This is why such a procedure can't exist in general in a language with first-class functions: the function which maps from names to values is many-to-one and thus has no inverse.
Here is an example of the sort of disgusting hack you could do to make this 'work' and also why it's horrible. The following is Racket-specific code:
(define-syntax define/naming
;; Define something in such a way that, if it's a procedure,
;; it gets the right name. This is a horrid hack.
(syntax-rules ()
[(_ (p arg ...) form ...)
(define (p arg ...) form ...)]
[(_ name val)
(define name (let ([p val])
(if (procedure? p)
(procedure-rename p 'name)
p)))]))
And now, given
(define/naming a
(let ([c 0])
(thunk
(begin0
c
(set! c (+ c 1))))))
(define/naming b a)
Then:
> (object-name a)
'a
> (object-name b)
'b
> (eqv? a b)
#f
> (a)
0
> (b)
1
> (a)
2
So a and b have the 'right' names, but because of that they are necessarily not the same object, which I think is semantically wrong: if I see (define a b) then I want (eqv? a b) to be true, I think. But a and b do capture the same lexical state, so that works, at least.

How to use continuations in scheme?

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.

Control flow in a Scheme program having call-with-current-continuation

I am new to Scheme programming and have been trying to understand the control flow of the programs having call-with-current-continuation in them. To be more specific, I want to know when the call to any continuation is invoked where is the control transferred and what happens after that. It would be really helpful if the below mentioned program is considered for the explanation.
(define call/cc call-with-current-continuation)
(define amb-exit '())
(define amb
(lambda ()
(call/cc
(lambda (m)
(call/cc
(lambda (f1)
(set! amb-exit (lambda () (f1 'exit)))
(m 1)))
(call/cc
(lambda (f2)
(set! amb-exit (lambda () (f2 'exit)))
(m 2)))
(call/cc
(lambda (f3)
(set! amb-exit (lambda () (f3 'exit)))
(m 3)))))))
(define back (lambda () (amb-exit)))
Now, I try to run the code this way (define a (amb)) and then I get the value in the terminal like this ;Value: a. Then in the terminal I check the value of a which returns me ;Value: 1. Then I call (back) I get a with the new value ;Value: 2. So on...
I know that when I do (define a (amb) the continuation f1 is invoked in the statement (set! amb-exit (lambda () (f1 'exit))) which transfers the control back to the first inner call/cc and the f1 continuation returns exit.
The thing that I am not able to understand is why the ;Value: a is ;Value: 1 instead of the value exit which is returned by f1?
The moment this part (f1 'exit) is executed, control returns back to first inner call/cc abandoning whatever (in this case (m 1)) after it.
So, this part (m 1) should never be invoked because the first inner continuation i.e. f1 is returned with exit even before hitting (m 1).
Any helpful comments regarding call-with-current-continuation in Scheme would also be appreciated.
Note: Using MIT/GNU Scheme
No, when you do (define a (amb)) the continuation f1 is not invoked, because it is behind (i.e. inside) a lambda.
No, (set! amb-exit (lambda () (f1 'exit))) sets amb-exit to the lambda function, and then the control passes to (m 1) which does invoke the continuation m. Which returns 1 from (amb) in (define a (amb)), which thus sets a to 1.
When you later call (back) it calls (amb-exit) which at that point invokes the f1 continuation with (f1 'exit) which returns the value 'exit from the (call/cc (lambda (f1) ...)) form. This value is discarded, and control passes into the (call/cc (lambda (f2) ...)) form, with similar effects.

evaluating my own functions from list with eval, R5RS

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

Execute the following call/cc expression

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).

Resources