[racket] understanding racket exceptions

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Sat Sep 4 10:24:19 EDT 2010

It looks like you have `call-with-exception-handler' right, but not
`with-handlers'.

The exception handler installed by `with-handlers' needs to escape to
the context of the `with-handlers' form before finding whether any
handlers apply. In other words, `with-handlers' combines a prompt
(using a generated tag) with a continuation mark. If no handler is
found, the exception should be re-raised using `raise'.

At Fri, 3 Sep 2010 14:05:43 -0400, Danny Yoo wrote:
> 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)))))
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> _________________________________________________
>   For list-related administrative tasks:
>   http://lists.racket-lang.org/listinfo/users


Posted on the users mailing list.