Let's start Scheme

2014-09-10

Is the condition continuable?

Since R6RS, Scheme has continuable exception which I think a good thing so that libraries may choose its behaviour when warning happened. R6RS has even the condition type &warning to let users know this. A bad thing is that, there is no way to know how the condition is raised. Think about this piece of code.
(import (scheme base) (scheme write))

(define-syntax safe-execute
  (syntax-rules ()
    ((_ expr ...)
     (with-exception-handler
      (lambda (e) #t)
      (lambda () expr ...)))))

(guard (e (else (display e) (newline) (raise e)))
  (safe-execute (raise "error huh?")))
The safe-execute just wraps given expression with with-exception-handler so that it can propagate non continuable condition to caller but can continue the process if the condition is just an warning. Now the problem is that, it doesn't propagate the raised condition as it is but modifies to something &non-continuable. For example, if you write the same code in R6RS and run it on Chez Scheme then the original condition's information disappears. (not sure if this is a bug of Chez though.)

So to keep my mind calm and mental health as healthy as possible, it is better to detect if the given condition is raised by raise or raise-continuable. However there is no way to do it with portable way. If you are an R6RS user, you may have slight hope, which is checking &warning or &non-continuable. If a script just wants to tell you an warning, then it usually raises an &warning condition. Thus checking this would make you a bit happy. Although, raise can take any Scheme object including &warning so this solution depends on the behaviour or philosophy of libraries. Moreover, guard without else needs to re-raise the caught condition with raise-continuable. This may cause something unexpected result if such a guard expression is wrapped by with-exception-handler.

Now, look at R7RS. It becomes totally hopeless. It doesn't have condition type, so the only hope that R6RS has is gone. The behaviour of all of related procedures and macro are the same as R6RS and it doesn't provide a procedures which can check if the condition is continuable or not, either. So this is totally depends on implementations.

If this is the case, then how the implementations behave. I've tested above piece of code with 4 implementations, Sagittarius, Chibi, Gauche and Foment. The results are followings;
  • Sagittarius - compounded the condition with &non-continuable
  • Chibi - changed condition with "exception handler returned" message (I guess)
  • Gauche - didn't print message at all (bug?)
  • Foment - propagated original condition.
For Gauche, if I changed raise to error it did print an error object. So it may not allow user to raise non condition object.

I'm not yet sure how important handling runtime exception is on Scheme. I've never written code that considers error other than just catching and logging. So this may be a trivial case.

3 comments:

Shiro Kawai said...

Good point. Actially I think it is wrong to distinguish continuable/non-continuable case by raise/raise-continuable; the information should be in a condition object.

Gauche's "raise" is actually srfi-18; it doesn't say anything about continuability, and relies solely on the exception handler to decide whether to continue or not. However, to prevent accidentally continuing into a fatal situation, Gauche's exception system checks if the condition inherits or not when the user-installed handler returns, and rejects to continue if it is. Because of this, our "raise-continuable" definition is just (define raise-continuable raise).

I should change r7rs#raise to check the returned handler to conform the standard, but I think srfi-18 design is more appropriate. Note that srfi-34's "raise" also relies on the handler to determine continuability, though it has slightly different semantics from srfi-18's regarding the dynamic environment. I feel I should've objected when R6RS tried to introduce raise-continuable...



Shiro Kawai said...

Oops, in the previous post, a class name surrounded by < and > gets deleted. The third sentence should read: Gauche's exception system checks if the condition inherits <serious-condition> or not ...

kei said...

I agree. And probably raise should take only condition object.

Post a Comment