[racket] macro for broken abstraction?

From: Alexander D. Knauth (alexander at knauth.org)
Date: Mon Dec 8 21:08:38 EST 2014

Would something like this work for what you want?

(define (boiler-plate/thunk thunk)
  (define err-msg #f)
  (let/ec fail-network
    (with-handlers ([exn:fail:network?
                     (lambda (e)
                       (set! err-msg "database connection error")
                       (fail-network err-msg))])
      (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 err-msg)))])
            (call-with-transaction db thunk #:isolation 'repeatable-read))
          (disconnect db)
          err-msg)))))

(define-syntax-rule (boiler-plate body ...)
  (boiler-plate/thunk (λ () body ...)))

(let ()
  (define err-msg
    (boiler-plate
     < body ... >
     ))
  ; err-msg needed here
  err-msg
  )


On Dec 8, 2014, at 6:45 PM, George Neuner <gneuner2 at comcast.net> wrote:

> 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
> 
> 
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20141208/d9a76459/attachment-0001.html>

Posted on the users mailing list.