[racket] understanding racket exceptions

From: Danny Yoo (dyoo at cs.wpi.edu)
Date: Fri Sep 3 14:05:43 EDT 2010

I'm about to try replicating Racket's exception system in WeScheme, so
I want to make sure I understood the model beforehand.  Here's my
understanding of the system, and if people can correct me, I'd greatly
appreciate it!


* Exception handlers are managed by keeping a chain of these handlers
within the continuation marks.  A form like with-handlers effectively
extends the chain within its dynamic extent.  There's a designated
continuation-marks key used to keep track of exception handlers.  This
key is internal and otherwise inaccessible to the outside world.  Let
me call this key "K" for the moment.  If it were possible to get at K,
then hypothetically I would be able to observe it by doing something
like this:

  (begin
    (printf "Before: ~s~n" (continuation-mark-set->list
(current-continuation-marks) K))
    (with-handlers ([exn void])
       (printf "Within: ~s~n" (continuation-mark-set->list
(current-continuation-marks) K)))


* Whenever an exception happens, the runtime immediately grabs the
current continuation marks and extracts the chain of exception
handlers associated to K.  It then walks the chain.  Each element in
the chain is responsible for returning the exception up the chain.  If
an exception handler knows what to do with an exception, and wants to
interrupt the chain, then it is supposed to call the
error-escape-handler to stop the rest of the computation.


I looked at the exceptions proposal [Friedman95
http://www.cs.indiana.edu/scheme-repository/doc.proposals.exceptions.html
].  I see that the proposal defines a current-exception-handlers, but
I don't see such a function in Racket.  I assume that the only way to
adjust the current exception handler is through
call-with-exception-handler.



If I wanted to simulate the exception-handling mechanism, it might
look something like this:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket

(define K (cons "my-distinguished"
                "exception key!"))


(define-syntax (simulate stx)
  (syntax-case stx ()
    [(_ body ...)
     (syntax/loc stx
       (call-with-exception-handler my-exception-handler
                                    (lambda ()
                                      body ...)))]))


(define (my-uncaught-exception-handler exn)
  (printf "I am the uncaught exception handler, seeing ~s~n" exn)
  (abort-current-continuation (default-continuation-prompt-tag) void))


(define (my-exception-handler exn)
  (printf "my-exception-handler\n")
  (let ([handlers
         (continuation-mark-set->list (current-continuation-marks)
                                      K)])
    (printf "I see the following handlers: ~s\n" handlers)
    (let ([exn
           (foldl (lambda (a-handler exn)
                    (a-handler exn))
                  exn
                  handlers)])

      ;; At this point, calls the uncaught-exception-handler if none of the
      ;; handlers will support us.
      (printf "Uncaught exception handler kicking in.\n")
      (my-uncaught-exception-handler exn)
      (printf "I should never see this!\n"))))


(define-syntax (my-with-handlers stx)
  (syntax-case stx ()
    [(_ ([exn-test? on-exn]
         ...)
        body ...)
     (syntax/loc stx
       ;; NOTE: I need to make sure the with-continuation-mark is not
in tail position,
       ;; or else my t2 shows that an exception handlers has been
replaced, rather than nested.
       (let ([result
              (with-continuation-mark K (lambda (exn)
                                          (cond
                                            [(exn-test? exn)
                                             (on-exn exn)
                                             (abort-current-continuation
                                              (default-continuation-prompt-tag)
                                              void)]
                                            ...
                                            [else
                                             exn]))

                (begin body ...))])
         result))]))


;; test1: see that I can catch an exception
(define (t1)
  (simulate (my-with-handlers ((exn:fail? (lambda (exn)
                                            (printf "Oh!  I see ~s~n" exn))))
                              (printf "Starting up~n")
                              (/ 1 0))))

;; test2: see that I can observe the nesting, and also fire off the
uncaught exception handler
(define (t2)
  (simulate (my-with-handlers ()
                              (printf "Starting up~n")
                              (my-with-handlers ()
                                                (/ 1 0)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Posted on the users mailing list.