try-catch
1 Introduction
2 Synopsis
3 Details
defatalize
try
8.12

try-catch🔗ℹ

David K. Storrs

 (require try-catch) package: try-catch

1 Introduction🔗ℹ

try-catch provides a combination of:

An additional macro, defatalize, is provided. It traps all raised values and returns them.

2 Synopsis🔗ℹ

> (require try-catch)
> (define err (defatalize (raise-arguments-error 'foo "failed")))
> err

(exn:fail:contract "foo: failed" #<continuation-mark-set>)

> (try [(displayln "ok")])

ok

> (try [(displayln "body")
        (raise 'boom)]
       [catch (string? (printf "caught a string\n"))
              (symbol? (printf "caught a symbol\n"))])

body

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))])

body

'e' (the value of the exception) is: 'boom

> (try [pre (displayln "pre")]
       [(displayln "body")]
       [post (displayln "post")])

pre

body

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")])))

pre

first body

post

> (down (void))

pre

second body

post

> (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")])))

pre

first body

post

> down+cleanup

#<procedure>

> (down+cleanup (void))

pre

second body

post

caught a symbol: boom

> down+cleanup

'symbol-caught

> (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" . 1707314790056.82) ("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

3 Details🔗ℹ

syntax

(defatalize body-expr ...+)

Evaluates the specified code. Exceptions and other raised values will be trapped and become the return value of the defatalize expression.

syntax

(try maybe-shared maybe-pre body maybe-post maybe-catch maybe-cleanup)

 
maybe-shared = 
  | [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))
Applies its clauses in order. The final value is the value returned by the body (if there are no errors), or of whichever catch clause is executed (if there are).

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.