David K. Storrs
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.
> err(exn:fail:contract "foo: failed"#<continuation-mark-set>)
ok
body
caught a symbol
body
'e' (the value of the exception) is: 'boom
pre
body
post
(updown))void ]pre
first body
post
pre
second body
post
(updown))void ]pre
first body
post
> down+cleanup#<procedure>
pre
second body
post
caught a symbol: boom
> down+cleanup'symbol-caught
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"username"name; Ensure that database handles are opened/closed as needed(query-user-data"fred"db)]opening a database handle
running a db query...
user is: '#hash(("ran-at" . 1763814717722.3743) ("user-id" . 6) ("username" . "bob"))
closing a database handle
done. final db handle value: #f
ignoring exception: interrupted before second query because it's quitting time
syntax
( defatalize body-expr...+)
syntax
( try maybe-sharedmaybe-prebodymaybe-postmaybe-catchmaybe-cleanup)
maybe-shared =maybe-pre =| [preexpr...+]body = [expr...+]maybe-post =| [postexpr...+]maybe-catch =| [catch(predicatehandler-expr...+)...+]maybe-cleanup =| [cleanupexpr...+]predicate = (and/cprocedure?(procedure-arity-includes/c1))
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. If you want cleanup code that is guaranteed to run, put it in the post clause.
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 one or more handler expressions. (cf with-handlers ) The handler expression will become the body of 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:
(try ['ok][catch(string? (displayln e)(displayln "done"))])
Is equvalent to this:
(with-handlers ([string? (lambda (e)(displayln e)(displayln "done"))])'ok)
If no catch clause is provided then all exceptions will be re-raised.