One of the things I don't want to see is stack traces. Whenever I saw this, I'd always be disappointed. There are basically 2 reasons: the first one is it usually means something went wrong, the second one is sometimes it doesn't show where the condition is actually raised. The first one can be considered a bug so I just need to snatch it. The second one is more for implementations issue.
Currently, Sagittarius shows stack trace whenever default exception handler is invoked, means no
guard
nor
with-exception-handler
. The stack trace is collected in the default exception handler. This is fine most of the time since I don't use them that much in my script. (well, I don't argue if it's good or not here.) However, sometimes I want a script fail safe or make sure it releases resources or so. Then I saw the fangs. As a simple example, you have the following script:
(guard (e (else (release-all) (raise e)))
(open-something-and-fail-it)
(release-all))
Suppose
open-something-and-fail-it
may raise an error and you want to release all resources after the process. Now, you saw a stack trace, what would you expect to be shown? I would expect where the root cause is raised. Or at least I can see it. However because the stack trace is collected in the default exception handler, you don't see those information at all.
It was okay until I had written a server program which shows a stack trace whenever unhandled exception is raised. If you write this kind of script, then you want to know which one is the actual culprit, server itself or the application program. As long as I'm writing both, it's just a matter of good old days printf debug but this is pain in the ass. If the stack trace shows where this happened, I don't have to do this type of thing in some cases. So I've changed the timing of collecting stack traces.
The basic idea is whenever
raise
or
raise-continuable
is called with compound condition, then it appends stack trace object. Only one stack trace object can be appeared in the compound condition. So if the condition is re-raised, then new stack trace object is created and the old one is set to the new one as its cause. It's pretty much similar with Java's exception. Of course, there are bunch of differences.
- Java's exception collects stack trace when it's created
java.lang.Throwable
's cause property may have root cause while Sagittarius' only takes stack trace object.
- The stack trace object is implicitly added, means given condition is copied entirely while Java's exception can be the same.
The item #1 is the biggest one. On Java, if you throw an exception created before the
throw
syntax, then the stack trace indicates where it's created. This can be very inconvenient like this type of case:
public class Foo {
private Exception exn = new Exception();
public void foo() throws Exception {
throw exn;
}
}
Don't ask me who would do this but the stack trace would be shown indicates the second line. (I didn't check the Java's spec, so this might depend on the implementation.)
The item #2 is because of implementation of
condition
. It flattens the given compound conditions if there are. For example, suppose you catch a condition and re-raise it with adding specific condition. The script would be like this:
;; suppose (fail) raises a compound condition
(guard (e (else (raise (condition (make-some-other-condition) e))))
(fail))
In this case, the compound condition
e will be flattened and merged into the new condition object. R6RS doesn't say how the
condition
procedure construct compound condition so it can decompose it by need.
The item #3 can be an issue. Suppose you want to re-use a condition and compare it by mean of
eq?
like this:
(let ((c (condition (make-error) (make-who-condition 'who))))
(guard (e (else (eq? c e)))
(raise c)))
;; -> #f
The
raise
procedure implicitly copy the given condition and adds stack trace object, thus the object wouldn't be the same one. I couldn't find anything mentioning this kind of thing on R6RS. So I think it's still R6RS compliant. However this might cause an issue in the future. (Using exception as a returning value or so?)
The reason why Sagittarius choose to collect stack trace when condition is raised is because of the requirement of
condition
procedure. It's not so difficult to make
condition
procedure collect stack trace however this procedure must be able to take arbitrary number of conditions including compound conditions. So there is no way to specify which one is the actual root cause when multiple compound conditions are given. (Nobody do this? who knows!)
I'm kinda doubting that if I made a better choice. Especially item #3. But seeing irrelevant stack traces is much more painful. So I think I can live with this.