Continuations and call/cc

25.1 What Is a Continuation?

At any point in a computation, the continuation is "what remains to be done with the current value". call-with-current-continuation (call/cc) packages the current continuation as a procedure k. Calling (k v) abandons whatever is happening and returns v to that saved point.

(+ 1 (call/cc (lambda (k) 10)))            ; 11: k isn't used, the lambda returns normally
(+ 1 (call/cc (lambda (k) (k 41) 1000)))   ; 42: calling k jumps out, skipping 1000
(* 2 (call/cc (lambda (k) (+ 100 (k 5))))) ; 10: the pending (+ 100 _) is abandoned

25.2 Early Exit

(define (find-first pred lst)
  (call/cc
    (lambda (return)
      (for-each (lambda (x) (when (pred x) (return x))) lst)
      #f)))
(find-first negative? '(3 1 -4 1 -5))      ; -4

(define (product lst)                      ; exit immediately on 0
  (call/cc
    (lambda (break)
      (let loop ((l lst))
        (cond ((null? l) 1)
              ((= (car l) 0) (break 0))
              (else (* (car l) (loop (cdr l)))))))))
(product '(1 2 0 4 5))                     ; 0, without multiplying the rest

25.3 Re-entering Continuations

Re-entry at the REPL behaves differently across implementations, because the "rest of the computation" includes the REPL's own loop. Experiment inside a single procedure to see clean behavior.

(define r #f)
(define count 0)
(define (test)
  (let ((v (call/cc (lambda (k) (set! r k) 1))))
    (set! count (+ count 1))
    (list 'value v 'count count)))
(test)              ; (value 1 count 1)
;; Later, re-entering the saved continuation re-runs the rest of TEST's body:
;; (r 5)            ; → (value 5 count 2), delivered to wherever (test) was originally called

25.4 Continuation-Passing Style (CPS)

Compilers often convert programs to CPS internally. With CPS, call/cc is trivial: it just passes k as a value.

;; Every procedure takes an explicit continuation k: control flow becomes data
(define (fact-cps n k)
  (if (= n 0)
      (k 1)
      (fact-cps (- n 1) (lambda (v) (k (* n v))))))
(fact-cps 5 (lambda (x) x))                ; 120

(define (safe-div-cps a b success failure)
  (if (= b 0) (failure "division by zero") (success (/ a b))))
(safe-div-cps 10 2 (lambda (v) (list 'ok v)) (lambda (e) (list 'error e)))   ; (ok 5)