[racket] macro for broken abstraction?
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>