[racket] raise vs abort

From: Casey Klein (clklein at eecs.northwestern.edu)
Date: Thu Jan 6 12:01:05 EST 2011

On Thu, Jan 6, 2011 at 10:08 AM, Keiko Nakata
<keiko at kurims.kyoto-u.ac.jp> wrote:
> On Thu, Jan 6, 2011 at 3:20 PM, Casey Klein <clklein at eecs.northwestern.edu> wrote:
>> > Still I am not certain that this behavior of 'raise' cannot be implemented
>> > by other (delimited) control operators. And, probably I do not still
>> > understand why 'raise' has to be a primitive.
>>
>> It can be implemented in terms of continuation marks (if you know the
>> key for exception handlers).
>
> You are probably saying 'raise' is different from 'abort' as 'raise' installs a barrier,
> which renders it to be a primitive.

No, that's not what I mean. `raise' can be instructed not to install
the barrier (via its optional second argument), and even in that mode,
it's still different than `abort'.

`raise' just calls the installed exception handlers in turn, until one
does not return. It does not unwind the stack as `abort' does. It's
something like the following (very lightly tested) code.

Note the second two test cases. In these, it's crucial that `raise'
does not unwind the stack, because it cannot be transparently rebuilt.

#lang racket

(require rackunit)

(define exception-handlers-key (gensym))
(define the-very-top (make-continuation-prompt-tag))

(define-syntax-rule (make-program p)
  (call-with-continuation-prompt (λ () p) the-very-top values))

(define (call-with-exception-handler handler thunk)
  ((λ (x) x) ; avoid clobbering current continuation's key
   (with-continuation-mark exception-handlers-key handler
     (thunk))))

(define uncaught-exception-handler
  (make-parameter
   (λ (exn)
     (fprintf (current-error-port) "uncaught exception ~s" exn)
     (abort-current-continuation the-very-top))))

(define (raise value)
  (let loop ([handlers (continuation-mark-set->list
                        (current-continuation-marks the-very-top)
                        exception-handlers-key)])
    (match handlers
      ['()
       (begin
         ((uncaught-exception-handler) value)
         (error 'raise "uncaught-exception-handler returned"))]
      [(cons h hs)
       (begin
         (call-with-continuation-barrier (λ () (h value)))
         (loop hs))])))

(define-syntax (test-program stx)
  (syntax-case stx ()
    [(_ program expected-results expected-output)
     #`(let* ([output (open-output-string)]
              [results (call-with-values
                        (λ ()
                          (parameterize ([current-output-port output])
                            (make-program program)))
                        list)])
         #,(syntax/loc #'expected-results
             (check-equal? results expected-results))
         #,(syntax/loc #'expected-output
             (check-equal? (get-output-string output) expected-output)))]))

(test-program
 (call-with-exception-handler
  (λ (_)
    (displayln "outer")
    (abort-current-continuation the-very-top))
  (λ ()
    (call-with-exception-handler
     (λ (_) (displayln "inner"))
     (λ ()
       (raise 3)))))
 (list)
 "inner\nouter\n")

(test-program
 (call-with-exception-handler
  (λ (exn)
    (displayln (continuation-mark-set->list (current-continuation-marks) 1))
    (abort-current-continuation the-very-top))
  (λ ()
    (with-continuation-mark 1 'a
      (call-with-continuation-barrier
       (λ () (raise 3))))))
 (list)
 "(a)\n")

(test-program
 (call-with-exception-handler
  (λ (exn)
    (displayln (continuation-mark-set->list (current-continuation-marks) 1))
    (abort-current-continuation the-very-top))
  (λ ()
    (with-continuation-mark 1 'a
      (dynamic-wind
       (λ () (displayln "enter"))
       (λ () (raise 3))
       (λ () (displayln "exit"))))))
 (list)
 "enter\n(a)\nexit\n")


Posted on the users mailing list.