Continuations issue - Shift vs Control

Hello,

I need to work with continuations. I read the documentation about it and is very well written. Only the documentation about the racket/control library is a bit cryptic. I needed to ask the AI for help and I think I’ve spotted a bug about the difference between the shift and control operators.

The difference between shift and control is that shift adds a reset automatically:

;control
(prompt val) => val
(prompt E[(control k expr)]) => (prompt ((lambda (k) expr)
                                         (lambda (v) E[v])))

;reset
(reset val) => val
(reset E[(shift k expr)]) => (reset ((lambda (k) expr)
                                     (lambda (v) (reset E[v]))))

But in this example there’s no difference:

(reset (+ 1 (shift k1 
              (k1 (+ 10 (shift k2 100))))))
              
;=> 100
              
(prompt (+ 1 (control k1 
               (k1 (+ 10 (control k2 100))))))
               
;=> 100

I think this is a bug. If I add a reset manually the output changes:

(reset (+ 1 (shift k1 
              (k1 (reset (+ 10 (shift k2 100)))))))
              
;=> 101
              
(prompt (+ 1 (control k1 
               (k1 (+ 10 (control k2 100))))))
               
;=> 100

If this is a bug I don’t know where to report it.

Matteo

It's not a bug. If I had to guess, I think your mistake is forgetting that k1 is "just" a function, and the arguments to a function are evaluated before the function is called. So yes, k1 contains its own reset wrapper, but it doesn't affect the evaluation of the second shift.

Here is how the first example reduces, roughly:

(reset (+ 1 (shift k1
              (k1 (+ 10 (shift k2 100))))))

;; Reduce the shift (k1).
;; E    = (+ 1 [ ])
;; expr = (k1 (+ 10 (shift k2 100)))

(reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
         (k1 (+ 10 (shift k2 100)))))

;; Reduce the shift (k2). (The body of the let is a function call,
;; and we must evaluate the argument before calling the function.)
;; E    = (let ([k1 ___]) (k1 (+ 10 [ ])))
;; expr = 100

(reset (let ([k2 (lambda (v2)
                   (reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
                            (k1 (+ 10 v2)))))])
         100))

;; Delete unused let bindings.

(reset 100)

;; Reduce the reset.

100

And here is how to the second example reduces:

(reset (+ 1 (shift k1
              (k1 (reset (+ 10 (shift k2 100)))))))

;; Reduce the shift (k1).
;; E    = (+ 1 [ ])
;; expr = (k1 (reset (+ 10 (shift k2 100))))

(reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
         (k1 (reset (+ 10 (shift k2 100))))))

;; Reduce the shift (k2). (The body of the let is a function call, 
;; so we must evaluate the argument before calling the function.)
;; E    = (+ 10 [ ])
;; expr = 100
;; within (reset (let ([k1 ___]) (k1 [ ])))

(reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
         (k1 (reset (let ([k2 (lambda (v2) (reset (+ 10 v2)))])
                      100)))))

;; Delete the unused let-binding of k2.

(reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
         (k1 (reset 100))))

;; Reduce the reset.

(reset (let ([k1 (lambda (v1) (reset (+ 1 v1)))])
         (k1 100)))

;; Apply k1.

(reset (reset (+ 1 100)))

;; Apply +.

(reset (reset 101))

;; Reduce the reset.

(reset 101)

;; Reduce the reset.

101

To make the intermediate steps easier to read, I've written the result of a shift reduction using let and kept the let around instead of doing substitution eagerly. I hope it's clear.

1 Like

Thank you very much.

Can you make an example where the differences between shift and control appear?

Matteo

In a shell:

$ racket

> (require  racket/control)

then

> ((lambda (x) (shift c (add1 (c (c 0))))) (shift k (k (k 0))))

versus

> ((lambda (x) (control c (add1 (c (c 0))))) (control k (k (k 0))))

(Be ready to ctrl-c. And you may wish to add println in some places.)

;; - - -

-- control reifies the continuation as a mathematical function, modulo the effects that the programmer "bakes in".

-- shift reifies the continuation as a procedure with a control (delimiter) effect added, plus the effects that the programmer bakes in.