Exception handlers Antti Huima (23 Oct 2000 09:14 UTC)
|
Re: Exception handlers
Marc Feeley
(05 Jan 2001 03:48 UTC)
|
Exception handlers Antti Huima 23 Oct 2000 09:14 UTC
It seems that this SRFI has overlived its draft period, so please throw this to /dev/null if you wish. Anyway, I think it is not beneficial that an exception handler can return to the point of throwing the exception. Assume that `open-input-file' raises an exception if a file is not found and let: (with-exception-handler (lambda (exception) (display "Funny thing happened") (newline)) (lambda () (let ((port (open-input-file "foo"))) (read port)))) Executing this obviously causes `port' to be bound to an undefined value, after which `read' will trigger an error that will typically be not an exception but a full-blown crash (sort of). I think that the common intuition w/ exception handlers is that they do not return in the standard way. Now, as shown in the document, the exception handler can invoke an explicitly captured escape continuation. However, if with-exception-handler would capture the continuation internally, its implementation could be possibly optimized. Moreover, this would inhibit the problem above appearing. Whatever exception systems will ever pop out, I would like to use such one that would define with-exception-handler as: (with-exception-handler handler thunk) ;procedure Returns the result(s) of (thunk), unless an exception is (raise)d, in which case the continuation waiting for the value of (raise) is discarced, (handler exception) is called, and the value(s) of this call is (are) returned. The number of values returned by (thunk) and by (handler exception) for any exception must match the number of values the continuation of the call to (with-exception-handler) expects to receive. Because I like this :-] I thus propose the draft SRFI-18 to be changed accordingly. I think it should be also specified that the dynamic extent of `thunk' is leaved when `handler' starts to execute. The document was not clear on this. Otherwise e.g. (raise) inside `handler' will have undefined behaviour. Here an example implementation (assuming single return values), assuming that the variable `handler' is bound to the current thread's exception handler that (raise) will invoke. `$$handler' is, in some magical way, a thread-local global variable (to avoid assuming fluid-lets or whatever): (define (with-exception-handler handler thunk) (let* ((unique (list #f)) (result (call-with-current-continuation (lambda (return) (let ((internal-handler (lambda (exc) (return (cons unique exc)))) (old-handler $$handler)) (dynamic-wind (lambda () (set! $$handler internal-handler)) thunk (lambda () (set! $$handler old-handler)))))))) (if (and (pair? result) (eq? (car result) unique)) (handler (cdr result)) result)))) This implementation restores the dynamic environment before handler starts to execute in the way the Scheme implementatino would restore it standardly after jumping to a captured continuation. As opposed to this, the example from the draft: (define (g) (call-with-current-continuation (lambda (return) (with-exception-handler (lambda (exc) (return (if (string? exc) (string-append "error: " exc) "unknown error"))) (lambda () (write (f 4.)) (write (f -1.)) (write (f 9.))))))) runs the handler in the dynamic environment of (thunk) which IMHO cannot be correct in general. -- Antti Huima