|(require try-catch)||package: try-catch|
try-catch provides a combination of:
The error handling from with-handlers, except with better end weight and less boilerplate
The protection of dynamic-wind
The option to share code between the clauses of the dynamic-wind
An extra happy-path cleanup phase after the dynamic-wind ends
An additional macro, defatalize, is provided. It traps all raised values and returns them.
> (require try-catch) > (define err (defatalize (raise-arguments-error 'foo "failed"))) > err
(exn:fail:contract "foo: failed" #<continuation-mark-set>)
> (try [(displayln "ok")])
> (try [(displayln "body") (raise 'boom)] [catch (string? (printf "caught a string\n")) (symbol? (printf "caught a symbol\n"))])
caught a symbol
> (try [(displayln "body") (raise 'boom)] [catch (string? (printf "caught a string: ~v\n" e)) (symbol? (printf "'e' (the value of the exception) is: ~v\n" e))])
'e' (the value of the exception) is: 'boom
> (try [pre (displayln "pre")] [(displayln "body")] [post (displayln "post")])
> (define down (let/cc up (try [pre (displayln "pre")] [(displayln "first body") (let/cc down (up down)) (displayln "second body") void] [post (displayln "post")])))
> (down (void))
> (define down+cleanup (let/cc up (try [pre (displayln "pre")] [(displayln "first body") (let/cc down (up down)) (displayln "second body") (raise 'boom) void] [post (displayln "post")] [catch (symbol? (and (printf "caught a symbol: ~a" e) 'symbol-caught))] [cleanup (displayln "cleanup")])))
> (down+cleanup (void))
caught a symbol: boom
> (try [shared (define username "bob")] [pre (printf "in pre, prepping to handle ~a.\n" username)] [(printf "in body. hello, ~a.\n" username)] [post (printf "in post, goodbye ~a.\n" username)] [cleanup (printf "in cleanup, done with ~a." username)])
in pre, prepping to handle bob.
in body. hello, bob.
in post, goodbye bob.
in cleanup, done with bob.
; Set up some mock functions for use in the following example
> (define (get-db-handle) (displayln "opening a database handle") 'mock-db-handle) > (define (close-dbh db) (displayln "closing a database handle") #f) > (define (is-quitting-time?) #t)
> (define (query-user-data name db) (printf "running a db query...\n") (hash "user-id" (random 10) "username" name "ran-at" (current-inexact-milliseconds))) ; Ensure that database handles are opened/closed as needed
> (try [shared (define db #f)] [pre (set! db (get-db-handle))] [(define user (query-user-data "bob" db)) (display "user is: ") (pretty-print user) (when (is-quitting-time?) (raise "interrupted before second query because it's quitting time")) (query-user-data "fred" db)] [post (set! db (close-dbh db)) (printf "done. final db handle value: ~a\n" db)] [catch (void (printf "ignoring exception: ~a" e))])
opening a database handle
running a db query...
user is: '#hash(("ran-at" . 1632872373218.6597) ("user-id" . 3) ("username" . "bob"))
closing a database handle
done. final db handle value: #f
ignoring exception: interrupted before second query because it's quitting time
(defatalize body-expr ...+)
(try maybe-shared maybe-pre body maybe-post maybe-catch maybe-cleanup)
| [shared expr ...+] maybe-pre =
| [pre expr ...+] body = [expr ...+] maybe-post =
| [post expr ...+] maybe-catch =
| [catch (predicate handler-expr) ...+] maybe-cleanup =
| [cleanup expr ...+] predicate = (and/c procedure? (procedure-arity-includes/c 1))
The code in the pre clause is invoked before body and the code in the post clause is invoked after body, regardless of how control enters/exits the body clause (e.g. due to a prompt abort or a continuation invocation). No special handling is performed for jumps into or out of the pre and post clauses.
Code in the shared clause is visible in all subsequent clauses. It is run only once, when the try expression is first entered.
Code in the cleanup clause is run only once, when the body clause completes normally. It is not run if an error is raised.
The error handling in the catch clause covers all the other clauses. It consists of a series of subclauses each of which consists of a predicate and a handler expression. (cf with-handlers) The handler expression will be wrapped in a one-argument procedure and the procedure will be called with the value of the exception being tested. The argument to this function is named e (short for ‘exception’) and is available in the handler.
For purposes of the catch clause, this:
Is equvalent to this:
If no catch clause is provided then all exceptions will be re-raised.