INTRODUCTION TO EXCEPTIONS (define (check-div1 n d) (cond [(= d 0) "Can't divide by zero"] [else (/ n d)])) (check-div1 3 0) (+ 5 (check-div1 3 0)) This code potentially gives the error in the wrong place. Should react to problem when error occurs. -------------------------------------------------------------- (define (check-div2 n d) (cond [(= d 0) (error 'check-div2 "Can't divide by zero")] [else (/ n d)])) This halts the entire program. What if programmer instead wants to prompt user for a new value for d instead of aborting (abort isn't always right decision -- what if writing critical code, say for plane controls -- if get error, can't just stop!). -------------------------------------------------------------- Exceptions provide structured jumps that exit code. Programmer controls where exit goes to. Two parts to exceptions: - flag that says "something went wrong" - handlers that say what to do when things go wrong (define (check-div3 n d) (cond [(= d 0) (raise 'div-zero-exn)] [else (/ n d)])) (with-handlers ([(lambda (exn) (symbol=? exn 'div-zero-exn)) (lambda (exn) "Oops -- divided by zero")]) (check-div3 4 0)) The basic Scheme constructs for exceptions: raise produces the flag, with handlers decide what to do when something goes wrong. (with-handlers (handler1 handler2 ...) body) where a handler is [predicate operation] -- each takes an exception: the predicate decides whether to handle it, the operation is called on the exception if the predicate returns true. the return value of the operation is the return value of the entire with-handlers if the predicate is true. -------------------------------------------------------------- Notice that this version is cleaner because is takes the cond out of the check-div, leaving the main program logic intact. (define (check-div-nonzero d) (if (= d 0) (raise 'div-zero-exn))) (define (check-div4 n d) (begin (check-div-nonzero d) (/ n d))) Now, consider two applications, each of which handles the exception in a different way: ;; something went wrong, let the user correct the problem (define (div n) (printf "Enter a denominator: ") (let ([d (read)]) (with-handlers ([(lambda (exn) (symbol=? exn 'div-zero-exn)) (lambda (exn) (printf "You tried to divide by zero. Try again.~n") (div n))]) (check-div4 n d)))) ;; something went wrong, let the programmer correct the problem (define (div-adjust n) (printf "Enter a denominator: ") (let ([result (with-handlers ([(lambda (exn) (symbol=? exn 'div-zero-exn)) (lambda (exn) 0)]) (check-div4 6 (read)))]) (+ result 7))) Finally, note that exceptions can be used to abort computation: (define (div-postpone n) (begin (with-handlers ([(lambda (exn) (symbol=? exn 'div-zero-exn)) (lambda (exn) (printf "Couldn't produce new result~n"))]) (begin (printf "Enter a denominator: ") (let ([result (check-div4 6 (read))]) (let ([new-result (+ result 7)]) (printf "The new result is ~a~n" new-result))))) (printf "All done~n"))) Once an exception is raised, control returns to the with-handlers code. Any unexecuted code in the scope of the with-handlers remains unexecuted.