main.ss
#lang scheme/base

(require (for-syntax scheme/base)
         scheme/list)

(provide lift-channel? (rename-out [build-lift-channel lift-channel]) lift-channel-live?
         lift capture)

(define-struct lift-channel (parameter))

(define (lift-channel-live? ch)
  (and ((lift-channel-parameter ch)) #t))

(define (lift channel value)
  (let ([p (lift-channel-parameter channel)])
    (cond
      [(p) => (lambda (previous)
                (p (cons value previous)))]
      [else (error 'lift "channel is not live")])))

(define-syntax (capture stx)
  (syntax-case stx ()
    [(capture (handler ...) e0 es ...)
     (with-syntax ([(ch ...) (for/list ([handler (syntax->list #'(handler ...))])
                               (syntax-case handler ()
                                 [[ch #:when pred?] #'ch]
                                 [[ch] #'ch]))])
       (with-syntax ([(ch-v ...) (generate-temporaries #'(ch ...))]
                     [(ch-p ...) (generate-temporaries #'(ch ...))]
                     [(ch-captured ...) (generate-temporaries #'(ch ...))]
                     [(ch-escaped ...) (generate-temporaries #'(ch ...))])
         (with-syntax ([(partition-e ...)
                        (for/list ([handler (syntax->list #'(handler ...))]
                                   [ch-p-id (syntax->list #'(ch-p ...))])
                          (with-syntax ([ch-p ch-p-id])
                            (syntax-case handler ()
                              [[_ #:when pred?] #'(partition pred? (ch-p))]
                              [[_] #'(values (ch-p) null)])))])
           #'(let ([ch-v ch] ...)
               (let ([ch-p (lift-channel-parameter ch-v)] ...)
                 (let-values ([(results ch-escaped ...)
                               (parameterize ([ch-p null] ...)
                                 (call-with-values
                                  (lambda () e0 es ...)
                                  (lambda results
                                    (let-values ([(ch-captured ch-escaped) partition-e] ...)
                                      (apply values (append results (list (reverse ch-captured) ...)) (list ch-escaped ...))))))])
                   (escape! ch-p ch-escaped) ...
                   (apply values results)))))))]))

(define (escape! parameter escaped-values)
  (cond
    [(parameter) => (lambda (previous)
                      (parameter (append escaped-values previous)))]))

(define build-lift-channel
  (let ([lift-channel (lambda ()
                        (make-lift-channel (make-parameter #f)))])
    lift-channel))