yield.ss
(module yield mzscheme
  
  (require (lib "contract.ss")
           (lib "cut.ss" "srfi" "26"))
  
  (provide yieldable)
  
  (provide/contract 
   [make-yieldable (-> procedure? procedure?)])
  
  ;; make-yieldable : (yield-procedure -> target-procedure)
  ;;               -> target-procedure
  ;;
  ;; where target-procedure and yield-procedure have symmetric
  ;; contracts:
  ;;
  ;;     target-procedure : a b c -> d e f
  ;;     yield-procedure  : d e f -> a b c
  ;;
  ;; The target procedure behaves like a normal procedure,
  ;; except that execution can be paused and resumed using the
  ;; yield procedure.
  ;;
  ;; Calling yield suspends execution of the target procedure,
  ;; and returns the values d, e and f to the caller.
  ;;
  ;; Subsequent calls to the target procedure resume execution
  ;; from the position of the last call to yield. The arguments
  ;; passed to the target are made available as the return values
  ;; of yield.
  (define (make-yieldable yield->body)
    ;; caller : (U continuation #f)
    ;; where continuation : any -> any
    (define caller #f)
    ;; resume : (U continuation #f)
    (define resume #f)
    ;; yield : d e f -> a b c
    (define (yield . args)
      (apply values 
             (let/cc k
               (set! resume k)
               (apply caller args))))
    ;; return : d e f -> a b c
    (define (return . args)
      (apply values 
             (let/cc k
               (set! resume #f)
               (apply caller args))))
    ;; body : a b c -> d e f
    (define body
      (yield->body yield))
    ; Main procedure body : a b c -> d e f
    (lambda args
      (let/cc k
        (set! caller k)
        (if resume
            (resume args)
            (call-with-values (cut apply body args)
                              return)))))

  ;; syntax (yieldable (id) stmt ...)
  (define-syntax (yieldable stx)
    (syntax-case stx ()
      [(_ yield statement ...)
       #'(make-yieldable (lambda (yield) statement ...))]))
  
  )