123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- ;; =========
- ;; UTILITIES
- ;; =========
- (define even-simpler-display
- (lambda (sth)
- (display
- (simple-format
- #f "~a\n" sth))))
- ;; ==========================
- ;; NON-CONTINUABLE EXCEPTIONS
- ;; ==========================
- (define erroroneous-proc-division
- (lambda (dividend divisor)
- ;; Division by zero will raise an uncontinuable exception. "uncontinuable"
- ;; means, that at here, at this very point, the execution cannot continue,
- ;; meaning, that erroroneous-proc-division does not know, what to do next,
- ;; when it receives a divisor equal to zero.
- (/ dividend divisor)))
- ;; Use an exception handler to handle the exception thrown when dividing by
- ;; zero. `with-exception-handler` takes 2 arguments. The installed exception
- ;; handler and the code to run wrapped in a lambda to avoid immediate
- ;; evaluation, called a "thunk".
- (with-exception-handler
- ;; The exception handler takes 1 argument, which is whatever is given to
- ;; `raise` or `raise-continuable` when the exception is raised. In the case
- ;; of dividing by zero, we get a `#<r6rs:record:&compound-condition>`, which
- ;; we can inspect.
- (lambda (condition-or-value)
- (display
- (simple-format
- #f
- (string-append "====\n"
- "condition or value:\n"
- "~a\n"
- "====\n")
- condition-or-value)))
- ;; The thunk is always a procedure taking 0 arguments. Its invocation is
- ;; running the code, which could cause an exception.
- (lambda ()
- ;; Divide by zero to cause an exception. Dividing by zero will result in a
- ;; non-continuable exception. The exception will be dealt with and the
- ;; program will then exit with an error.(simple-conditions condition)
- (erroroneous-proc-division 1 0))
- ;; From the Guile reference manual:
- ;; "[...] it’s often the case that one would like to handle an exception by
- ;; unwinding the computation to an earlier state and running the error handler
- ;; there. After all, unless the raise-exception call is continuable, the
- ;; exception handler needs to abort the continuation. To support this use
- ;; case, if with-exception-handler was invoked with #:unwind? #t is true,
- ;; raise-exception will first unwind the stack by invoking an escape
- ;; continuation (see call/ec), and then invoke the handler with the
- ;; continuation of the with-exception-handler call." --
- ;; https://www.gnu.org/software/guile/docs/master/guile.html/Raising-and-Handling-Exceptions.html
- ;; Here the exception is non-continuable, so according to the reference
- ;; manual, we need to call with-exception-handler with #:unwind? being set to
- ;; #t for normal exception handling. If we do not do this, a (different?)
- ;; non-continuable exception is raised.
- ;; Q: Why unwind the stack?
- ;; A:
- ;; If the execution cannot be continued (non-continuable exception) at the
- ;; point, where the exception is raised, it means, that in that context, there
- ;; was insufficient information to continue the execution in useful way.
- ;; Unwinding the stack means discarding stack frames of procedure calls. This
- ;; happens up to the point, where an exception handler is defined. The result
- ;; of unwinding the stack is, that we only leave intact the environment, which
- ;; was available when the exception handler was defined.
- ;; We have the knowledge at this point, that an exception occurred. That is
- ;; more than we knew, when we called the procedure, which raised the
- ;; exception. Furthermore the exception can contain information about the kind
- ;; of thing that went wrong. This information hand-over facility should be
- ;; used to give sufficient information to the exception handler, so that the
- ;; handler can continue execution in a useful way. The exception enables us to
- ;; store important information from the environment at the point where the
- ;; exception was raised. Then we have no need to keep the environment of the
- ;; point, where the exception was raised and can unwind the stack.
- #:unwind? #t)
- ;; Lets see what happens, if we do not set #:unwind? to #t. (Note: This example
- ;; should result in an exception, even though we are using
- ;; with-exception-handler, because we are doing it the wrong way.)
- (with-exception-handler
- (lambda (condition-or-value)
- (display
- (simple-format
- #f
- (string-append "====\n"
- "condition or value:\n"
- "~a\n"
- "====\n")
- condition-or-value)))
- (lambda ()
- (erroroneous-proc-division 1 0))
- ;; #:unwind? #f is the default.
- #:unwind? #f)
- ;; As described above, a non-continuable exception is raised and the program
- ;; breaks, which is usually not what one wants in many cases.
- ;; ======================
- ;; CONTINUABLE EXCEPTIONS
- ;; ======================
- ;; Here is another procedure, which raises a continuable exception. When it is
- ;; dealt with, the program will continue and not exit with the error.
- (define erroroneous-proc-2
- (lambda ()
- (raise-continuable "I am an error.")))
- (with-exception-handler
- (lambda (condition-or-value)
- (display (simple-format #f "~a\n" condition-or-value)))
- (lambda ()
- (erroroneous-proc-2)))
|