[racket] macro for broken abstraction?
On Dec 9, 2014, at 7:40 AM, Gustavo Massaccesi <gustavo at oma.org.ar> wrote:
> 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.
But this works:
#lang racket
(require racket/stxparam)
(define-syntax-parameter thing #f)
(define x #f)
(syntax-parameterize ([thing (make-rename-transformer #'x)])
(set! thing 5))
x ; 5
> 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
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users