[racket] macro for broken abstraction?

From: George Neuner (gneuner2 at comcast.net)
Date: Mon Dec 8 18:45:39 EST 2014

Hi all,

This is more curiosity than a problem.

I am writing a web service which uses a database and there is a fair bit 
of boilerplate exception handling code which is needed in just about 
every servlet.  It takes up a lot of page and indentation space in the 
source and just offends my sensibilities.  I have created a macro to 
condense the visual, but I'm not entirely happy with it because of how 
it deals with interactions among the template, the body and the 
surrounding code.

As used inline, everything I am doing fits into the following pattern:

   (let [
         (err-msg #f)
        ]

     :

     ; --- start boilerplate
     (let/ec fail-network
       (with-handlers [
                       (exn:fail:network?
                        (lambda (e)
                          (set! err-msg "database connection error")
                          (fail-network)))
                      ]
         (let [
               (db (connect-database))
              ]

           (let/ec fail-sql
             (with-handlers [
                             (exn:fail:sql?
                              (lambda (e)
                                (let [(info (exn:fail:sql-info e))]
                                  (set! err-msg (cdr (assoc 'message info)))
                                  (fail-sql))))
                            ]

               (call-with-transaction db
                 (lambda ()
                   :
                   < body ... >
                   :
                   )
                 #:isolation 'repeatable-read)

               )

           (disconnect db))

         )))
     ; --- end boilerplate


     ; err-msg needed here

     )


The problems, of course, are that "err-msg" is external to the 
boilerplate code, and "db" is internal to it.   The exception handlers 
in the boilerplate need access to "err-msg" (or whatever the actual 
variable name might be)  and code in the  <body> needs to reference "db" 
which is only defined within the scope of the macro.

I've gotten it to work with syntax-rules by passing the problem names in 
as arguments:

(define-syntax with-database
   (syntax-rules ()
     ((with-database-connection db err-msg body ...)
        ... )))

knowing that I can use any names externally or in the body (the above is 
just example).

This works, but having to pass in the (name of the) required error 
status variable is ugly.  I know I can add a keyword (or several) to 
handle the status variable, but reasonable syntaxes for it are 
convoluted.  Something like  "with-database-connection <JOE> sending 
errors to <BOB> ..."  obviously is possible, but is unwieldy (and 
un-Scheme-like  [ un-Schemely? ] ) - it looks more like Smalltalk or 
Lisp's loop language than it does like Scheme. 8-)

I suppose I could require to pass in exception handler functions rather 
than a target for their output, but that also is unwieldy and error 
prone because the functions have to be inserted at different nesting levels.

Is there some idiomatic way people normally handle this kind of broken 
abstraction?   I've searched a bit trying to find a discussion of 
something like this, but so far haven't found anything enlightening.

Thanks,
George



Posted on the users mailing list.