## Module (chicken condition)

This module provides various procedures and special forms for raising and handling exceptions with "condition objects". Condition objects provide a structured and composable way to encode the kind of exception that took place, and provide the necessary context.

CHICKEN's exception handling is based on the SRFI-12 exception system. This document contains the core of the SRFI-12 spec as well as CHICKEN implementation specifics.

There is also a `srfi-12` or `(srfi 12)` module which only includes the standard procedures and macros from the SRFI document, without the CHICKEN extensions. `(chicken condition)` offers the complete set of procedures and macros, both CHICKEN-specific and standard SRFI-12.

## CHICKEN implementation

### System conditions

All error-conditions signaled by the system are of kind `exn`. The following composite conditions are additionally defined:

(exn arity) | Signaled when a procedure is called with the wrong number of arguments.
(exn type) | Signaled on type-mismatch errors, for example when an argument of the wrong type is passed to a built-in procedure.
(exn arithmetic) | Signaled on arithmetic errors, like division by zero.
(exn i/o) | Signaled on input/output errors.
(exn i/o file) | Signaled on file-related errors.
(exn i/o net) | Signaled on network errors.
(exn bounds) | Signaled on errors caused by accessing non-existent elements of a collection.
(exn runtime) | Signaled on low-level runtime-system error-situations.
(exn runtime limit) | Signaled when an internal limit is exceeded (like running out of memory).
(exn match) | Signaled on errors raised by failed matches (see the section on `match`).
(exn syntax) | Signaled on syntax errors.

### Notes

- All error-exceptions (of the kind `exn`) are non-continuable.
- Error-exceptions of the `exn` kind have additional `arguments` and `location` properties that contain the arguments passed to the exception-handler and the name of the procedure where the error occurred (if available).
- Within the interpreter (csi), a user-interrupt (`signal/int`) signals an exception of the kind `user-interrupt`.
- The procedure `condition-property-accessor` accepts an optional third argument. If the condition does not have a value for the desired property and if the optional argument is given, no error is signaled and the accessor returns the third argument.
- On platforms that support the `sigprocmask(3)` POSIX API function, the signals `SIGSEGV`, `SIGFPE`, `SIGBUS` and `SIGILL` will be caught and trigger an exception instead of aborting the process, if possible. If the unwinding and handling of the signal raises one of these signals once again, the process will abort with an error message.

### Additional API

#### condition-case

`(condition-case EXPRESSION CLAUSE ...)`syntax

  Evaluates `EXPRESSION` and handles any exceptions that are covered by `CLAUSE ...`, where `CLAUSE` should be of the following form:

       CLAUSE = ([VARIABLE] (KIND ...) BODY ...)

  If provided, `VARIABLE` will be bound to the signaled exception object. `BODY ...` is executed when the exception is a property- or composite condition with the kinds given `KIND ...` (unevaluated). If no clause applies, the exception is re-signaled in the same dynamic context as the `condition-case` form.

      (define (check thunk)
        (condition-case (thunk)
          [(exn file) (print "file error")]
          [(exn) (print "other error")]
          [var () (print "something else")] ) )

      (check (lambda () (open-input-file "")))   ; -> "file error"
      (check (lambda () some-unbound-variable))  ; -> "othererror"
      (check (lambda () (signal 99)))            ; -> "something else"

      (condition-case some-unbound-variable
        ((exn file) (print "ignored")) )      ; -> signals error

#### get-condition-property

get-condition-property CONDITION KIND PROPERTY #!optional DEFAULT procedure

  A slightly more convenient condition property accessor, equivalent to

      ((condition-property-accessor KIND PROPERTY [DEFAULT]) CONDITION)

#### condition

condition LST1 LST2 ...procedure

  This is a more convenient constructor for conditions. Each of LST1, LST2 etc is a list of the following form:

       (KIND PROPERTY1 VALUE1 PROPERTY2 VALUE2 ...)

  In other words, the following:

       (signal (condition '(exn location foo message "hi") '(file bar 1)))

  is equivalent to the SRFI-12 code:

       (signal (make-composite-condition
                 (make-property-condition 'exn 'location 'foo 'message "hi")
                 (make-property-condition 'file 'bar 2)))

#### condition->list

condition->list CONDITION procedure

  This procedure converts a condition object into a list holding all the conditions that are represented by the CONDITION object. It is formatted as follows:

      ((KIND1 PROPERTY1 VALUE1 PROPERTY2 VALUE2 ...) (KIND2 ... ) ... )

  There is no guaranteed order within the list.

#### print-error-message

print-error-message EXN #!optional PORT HEADER procedure

  Prints an appropriate error message to PORT (which defaults to the value of `(current-output-port)` for the object EXN. EXN may be a condition, a string or any other object. The output is prefixed by the HEADER, which defaults to `"Error:"`.

## SRFI-12 specification

A Scheme implementation ("the system") raises an exception whenever an error is to be signaled or whenever the system determines that evaluation cannot proceed in a manner consistent with the semantics of Scheme. A program may also explicitly raise an exception.

Whenever the system raises an exception, it invokes the current exception handler with a condition object (encapsulating information about the exception) as its only argument. Any procedure accepting one argument may serve as an exception handler. When a program explicitly raises an exception, it may supply any object to the exception handler.

An exception is either continuable or non-continuable. When the current exception handler is invoked for a continuable exception, the continuation uses the handler's result(s) in an exception-specific way to continue. When an exception handler is invoked for a non-continuable exception, the continuation raises a non-continuable exception indicating that the exception handler returned. On CHICKEN, system error exceptions (of kind `exn`) are non-continuable.

### Exception Handlers

#### current-exception-handler

current-exception-handler #!optional PROCEDURE parameter

  Sets or returns the current exception handler, a procedure of one argument, the exception object.

#### with-exception-handler

with-exception-handler handler thunk procedure

  Returns the result(s) of invoking thunk. The handler procedure is installed as the current exception handler in the dynamic context of invoking thunk.

  Example:

      (call-with-current-continuation
       (lambda (k)
        (with-exception-handler (lambda (x) (k '()))
                                (lambda () (car '())))))
      ;=> '()

  Note that the handler procedure must somehow return non-locally out of the dynamic extent of the `with-exception-handler` form, because returning normally will signal yet another exception and thus result in non-termination.

#### handle-exceptions

`(handle-exceptions var handle-expr expr1 expr2 ...)`syntax

  Evaluates the body expressions expr1, expr2, ... in sequence with an exception handler constructed from var and handle-expr. Assuming no exception is raised, the result(s) of the last body expression is(are) the result(s) of the `handle-exceptions` expression.

  The exception handler created by `handle-exceptions` restores the dynamic context (continuation, exception handler, etc.) of the `handle-exceptions` expression, and then evaluates handle-expr with var bound to the value provided to the handler.

  Examples:

      (handle-exceptions exn
      		   (begin
      		     (display "Went wrong")
      		     (newline))
       (car '()))
      ; displays "Went wrong"

      (handle-exceptions exn
      		   (cond
      		    ((eq? exn 'one) 1)
      		     (else (abort exn)))
        (case (random 3)
         [(0) 'zero]
         [(1) (abort 'one)]
         [else (abort "Something else")]))
      ;=> 'zero, 1, or (abort "Something else")

### Raising Exceptions

#### abort

abort obj procedure

  Raises a non-continuable exception represented by obj. The `abort` procedure can be implemented as follows:

      (define (abort obj)
        ((current-exception-handler) obj)
        (abort (make-property-condition
      	   'exn
      	   'message
      	   "Exception handler returned")))

  The `abort` procedure does not ensure that its argument is a condition. If its argument is a condition, `abort` does not ensure that the condition indicates a non-continuable exception.

#### signal

signal obj procedure

  Raises a continuable exception represented by obj. The `signal` procedure can be implemented as follows:

      (define (signal exn)
       ((current-exception-handler) exn))

  The `signal` procedure does not ensure that its argument is a condition. If its argument is a condition, `signal` does not ensure that the condition indicates a continuable exception.

### Condition Objects

#### condition?

condition? obj procedure

  Returns #t if obj is a condition, otherwise returns #f. If any of the predicates listed in Section 3.2 of the R5RS is true of obj, then `condition?` is false of obj.

  Rationale: Any Scheme object may be passed to an exception handler. This would cause ambiguity if conditions were not disjoint from all of Scheme's standard types.

#### make-property-condition

make-property-condition kind-key prop-key value ...procedure

  This procedure accepts any even number of arguments after kind-key, which are regarded as a sequence of alternating prop-key and value objects. Each prop-key is regarded as the name of a property, and each value is regarded as the value associated with the key that precedes it. Returns a kind-key condition that associates the given prop-keys with the given values.

#### make-composite-condition

make-composite-condition condition ...procedure

  Returns a newly-allocated condition whose components correspond to the the given conditions. A predicate created by `condition-predicate` returns true for the new condition if and only if it returns true for one or more of its component conditions.

#### condition-predicate

condition-predicate kind-key procedure

  Returns a predicate that can be called with any object as its argument. Given a condition that was created by `make-property-condition`, the predicate returns #t if and only if kind-key is EQV? to the kind key that was passed to `make-property-condition`. Given a composite condition created with `make-composite-condition`, the predicate returns #t if and only if the predicate returns #t for at least one of its components.

#### condition-property-accessor

condition-property-accessor kind-key prop-key #!optional default procedure

  Returns a procedure that can be called with any condition that satisfies `(condition-predicate ''kind-key'')`. Given a condition that was created by `make-property-condition` and kind-key, the procedure returns the value that is associated with prop-key. Given a composite condition created with `make-composite-condition`, the procedure returns the value that is associated with prop-key in one of the components that satisfies `(condition-predicate ''kind-key'')`.

  On CHICKEN, this procedure accepts an optional third argument DEFAULT. If the condition does not have a value for the desired property and if the optional argument is given, no error is signaled and the accessor returns the third argument.

  When the system raises an exception, the condition it passes to the exception handler includes the `'exn` kind with the following properties:

  message
    the error message
  arguments
    the arguments passed to the exception handler
  location
    the name of the procedure where the error occurred (if available)

  Thus, if exn is a condition representing a system exception, then

       ((condition-property-accessor 'exn 'message) exn)

  extracts the error message from exn. Example:

      (handle-exceptions exn
      		   (begin
      		     (display "Went wrong: ")
      		     (display
      		      ((condition-property-accessor 'exn 'message) exn))
      		     (newline))
       (car '()))
      ; displays something like "Went wrong: can't take car of nil"

### More Examples

    (define (try-car v)
     (let ((orig (current-exception-handler)))
       (with-exception-handler
        (lambda (exn)
          (orig (make-composite-condition
    	     (make-property-condition
    	      'not-a-pair
    	      'value
    	      v)
    	     exn)))
        (lambda () (car v)))))

    (try-car '(1))
    ;=> 1

    (handle-exceptions exn
    		   (if ((condition-predicate 'not-a-pair) exn)
    		       (begin
    			(display "Not a pair: ")
    			(display
    			 ((condition-property-accessor 'not-a-pair 'value) exn))
    			(newline))
    		       (abort exn))
      (try-car 0))
    ; displays "Not a pair: 0"

    (let* ((cs-key (list 'color-scheme))
           (bg-key (list 'background))
           (color-scheme? (condition-predicate cs-key))
           (color-scheme-background
    	(condition-property-accessor cs-key bg-key))
           (condition1 (make-property-condition cs-key bg-key 'green))
           (condition2 (make-property-condition cs-key bg-key 'blue))
           (condition3 (make-composite-condition condition1 condition2)))
      (and (color-scheme? condition1)
           (color-scheme? condition2)
           (color-scheme? condition3)
           (color-scheme-background condition3)))
    ; => 'green or 'blue

Previous: Module (chicken blob)

Next: Module (chicken continuation)