Schematic: what is the difference between define and let when used with continuation

I am interested in the difference between the two following codes:

(define cont2 #f) (call/cc (lambda (k) (set! cont2 k))) (display "*") (cont2 #f) 

and

 (let [(cont #f)] (call/cc (lambda (k) (set! cont k))) (display "*") (cont #f)) 

In my opinion, the correct behavior of these two programs should print "*" endlessly. However, the first one prints only one '*' and exits, and the second gives the correct behavior.

So I'm confused. Is there anything special with define or a continuation - this is not what I thought - all the following programs until the end of the program seem to have a border or something like that.

Another hunch is that the upper level of the environment is specially processed, for example:

 (define (test) (define cont2 #f) (call/cc (lambda (k) (set! cont2 k))) (display "*") (cont2 #f)) (test) 

It works, but why?

Thank you for your help!

+4
source share
4 answers

In Racket, each top-level expression is wrapped in a prompt .

Since call/cc only "captures the current continuation to the nearest prompt", in your first example, none of the other tops, so applying cont2 to #f results in just #f .

Also, moving the first example to begin will not change the value, since the top level begin implicitly binds its contents, as if they were top-level expressions.

+8
source

When you are at the top level, continue (note the invitation symbol '>'):

 > (call/cc (lambda (k) (set! cont2 k))) 

is the top level of read-eval-print-loop. That is, in the first code fragment, you enter the expressions one by one, returning to the top level after each. If you did:

 (begin (define cont3 #f) ... (cont3 #f)) 

you will get infinite * (because you returned to the top level only when begin complete). Your third piece of code is an example of this; you get infinite * because continuation is not a top level loop.

+4
source

This is not only in your opinion. If you do this in R5RS or R6RS , both do not behave the same, this is a report violation. In racket (r5rs implementation), it probably breaks, since I checked them with plt-r5rs , and it obviously doesn't loop forever.

#lang racket (the default language for the implementation of racket ) does not comply with any standard, therefore, like perl5 , how it behaves is a specification. Their documentation writes a lot about tag hints , which reduces the amount of continuation.

arguments against call / cc that come to mind when reading this question. I think the interesting part is:

It is said that call / cc implemented in real Scheme systems never captures the whole continuation in any case: many Scheme systems are an implicit control delimiter around REPL or threads. These implicit delimiters are easily noticeable: for example, in Petite Chez or Scheme 48, code

  (let ((k0 #f)) (call/cc (lambda (k) (set! k0 k))) (display 0) (k0 #f)) 

prints an endless stream of zeros. If we put each operation on it (evaluated by its own REPL):

  (define k0 #f) (call/cc (lambda (k) (set! k0 k))) (display 0) (k0 #f) 

the output is simple 0 #f.

I'm not sure that I am against call/cc as a primitive (I believe that your tools should give you the opportunity to shoot in the foot). I feel that I can change my mind after writing the Scheme compiler, although I will return to it when I find it.

+2
source

It also works, don't ask me why. (Call / cc just blowing my mind)

 (define cont2 #f) ((lambda () (call/cc (lambda (k) (set! cont2 k))) (display "*") (cont2 #f))) 

In your test, define three lines inside the implicit start that you call together, which is called at the top level. In my version, I just made an anonymous thunk, which is called by myself. In your first definition of cont2, your define simply creates a placeholder for the value, and your function calls afterwards are not connected to each other inside and in the environment,

0
source

Source: https://habr.com/ru/post/1495670/


All Articles