Disclaimer
This is not community approved best practice nor it can be applied to the latest standard (R7RS). So I don't have any responsibility for your implementation can't handle or claims that nobody is writing this type of code :p
Basics
Condition system
R6RS has a very nice, (yet it was very controversial), the feature called condition system. It is built on top of, again very controversial, record type system. The basic ideas are:
- Conditions are inheritable (the absolute base is
&condition
) - Conditions are compoundable. (using
condition
procedure)
How to use
The very basic usage is like this:
(define-condition-type &foo &error make-foo-error foo-error?)This defines a condition type of
&foo
. Then you can signal the condition with raise
or raise-continuable
procedure.My practices
Currently, I'm using conditions with the following rules, which I think the best at this moment.
- Should create at least one library specific condition
If you are creating a library namedfoo
, then the library should have a specific condition such as&foo
unless the library provides only utilities. (e.g.(srfi :1)
provides only list utilities). - Must not use the
error
procedure
If you see the standarderror
, then it's a bad sign. The standard conditions are good for general purposes but not good for a library specific error signalling. - Should split conditions per phases
If a library has several phases, then the conditions should be split. For example,(text json jmespath)
has compilation and evaluation phases. So the condition should be split into 2, one for compilation time, the other one for evaluation time. - Must not put too many fields
Conditions are records, thus users can put as many as fields onto it. A condition must contain minimum information or resources. If you need more information, then use&irritants
- Should use composite then inheritance
Conditions are records (I'm saying it twice because it's important), means you can only inherit one base condition. However, sometimes you want to put meta information such as&i/o
. In this case use thecondition
procedure instead of creating a new condition type which inherits&i/o
.For example, suppose you have&foo
condition, which inherits&error
. Now your library should also signale&i/o
when an I/O operation failed. Theoretically, you have the following 2 options:- Composite
&i/o
instance - Create a new condition type which inherits
&i/o
&i/o
, then you should use option number 1. In this manner, library users can handle the error situation easier by just adding a guard clause withfoo-error?
(suppose your condition predicate of&foo
isfoo-error?
). And users can still check I/O error withi/o-error?
- Composite
Conclusion
I'm quite happy with the above rules whenever I use the libraries constructed with it (sort of dogfooding).