[racket] macro for broken abstraction?
I think that you want to capture the variables. I made a simplified
example. The other variables (for example "hidden") are not capture,
as usual.
;--- boilerplate.rkt ---
#lang racket
(define (call-with-?? proc) (proc)); just for the example
(define exn:fail:network? exn:fail?); just for the example
(provide with-db)
(define-syntax with-db
(lambda (stx)
(syntax-case stx ()
[(_ body ...)
(with-syntax ([err-msg (datum->syntax stx 'err-msg stx stx)]
[db (datum->syntax stx 'db stx stx)])
; --- start boilerplate
(syntax-protect
(syntax/loc stx
(let/ec fail-network
(with-handlers [(exn:fail:network? (lambda (e)
(set! err-msg
"database connection error")
(fail-network)))]
(define db 5)
(define hidden 7)
(call-with-?? (lambda () body ...))))))
; --- end boilerplate
)])))
;--- main.rkt
#lang racket
(require "boilerplate.rkt")
(define err-msg #f)
(define hidden "BU")
(with-db
(displayln db); ==> 5
(displayln hidden); ==> "BU"
(/ 1 0)
(display "never"))
(when err-msg
(error 'error err-msg))
;-------
There is a big red warning in the Racket documentation about macros
with variable capture, but I can't find it. It recommends to use
syntax-parameters, but the syntax parameter can't change the value of
err-msg after the macro body had finished. The problem is that if you
use the with-db macro inside a with-super-db macro, the results are
strange.
Gustavo
On Mon, Dec 8, 2014 at 8: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